root/cygnal/libamf/element.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _type
  2. _type
  3. _type
  4. _type
  5. _type
  6. _type
  7. _type
  8. _type
  9. _referenceid
  10. init
  11. clear
  12. getDataSize
  13. to_number
  14. to_short
  15. to_integer
  16. to_string
  17. to_bool
  18. to_reference
  19. to_reference
  20. calculateSize
  21. calculateSize
  22. encode
  23. encode
  24. makeString
  25. makeNullString
  26. makeString
  27. makeString
  28. makeString
  29. makeNumber
  30. makeNumber
  31. makeNumber
  32. makeNumber
  33. makeNumber
  34. makeBoolean
  35. makeBoolean
  36. makeBoolean
  37. makeUndefined
  38. makeUndefined
  39. makeNull
  40. makeNull
  41. makeObject
  42. makeObject
  43. makeObject
  44. makeObject
  45. makeXMLObject
  46. makeXMLObject
  47. makeXMLObject
  48. makeXMLObject
  49. makeObjectEnd
  50. makeTypedObject
  51. makeTypedObject
  52. makeTypedObject
  53. makeReference
  54. makeReference
  55. makeReference
  56. makeMovieClip
  57. makeMovieClip
  58. makeECMAArray
  59. makeECMAArray
  60. makeECMAArray
  61. makeECMAArray
  62. makeStrictArray
  63. makeStrictArray
  64. makeStrictArray
  65. makeStrictArray
  66. makeUnsupported
  67. makeUnsupported
  68. makeLongString
  69. makeLongString
  70. makeRecordSet
  71. makeRecordSet
  72. makeDate
  73. makeDate
  74. makeDate
  75. getNameSize
  76. setName
  77. setName
  78. setName
  79. check_buffer
  80. dump
  81. findProperty

// amf.cpp:  AMF (Action Message Format) rpc marshalling, for Gnash.
// 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
//   2011 Free Software Foundation, Inc
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include <string>
#include <vector>
#include <cmath>
#include <climits>
#include <boost/shared_ptr.hpp>
#include <boost/cstdint.hpp> // for boost::?int??_t

#include "buffer.h"
#include "log.h"
#include "amf.h"
#include "amfutf8.h"
#include "utility.h"
#include "element.h"
#include "GnashException.h"

using std::string;
using gnash::log_error;

/// \namespace cygnal
///
/// This namespace is for all the AMF specific classes in libamf.
namespace cygnal
{

/// \brief This is used to print more intelligent debug messages
const char *astype_str[] = {
    "Number",
    "Boolean",
    "String",
    "Object",
    "MovieClip",
    "Null",
    "Undefined",
    "Reference",
    "ECMAArray",
    "ObjectEnd",
    "StrictArray",
    "Date",
    "LongString",
    "Unsupported",
    "Recordset",
    "XMLObject",
    "TypedObject",
    "AMF3 Data"
};

/// \brief Create a new Element with no data type.
Element::Element()
    : _name(0),
      _type(NOTYPE)
{
//    GNASH_REPORT_FUNCTION;
}

/// \brief Delete this Element
///             This deallocates all the memory used to hold the data.
Element::~Element()
{
//    GNASH_REPORT_FUNCTION;
    delete[] _name;
}

/// \brief Construct an AMF Element from a double..
///
/// @param data The double to use as the value for this Element.
Element::Element(double indata)
    : _name(0),
      _type(NOTYPE)
{
//    GNASH_REPORT_FUNCTION;
    makeNumber(indata);
}

/// \brief Contruct a Property from a double.
///
/// @param name The name of the Property
///
/// @param num The double to use as the value of the property.
Element::Element(const string &name, double num)
    : _name(0),
      _type(NOTYPE)
{
//    GNASH_REPORT_FUNCTION;
    makeNumber(name, num);
}

/// \brief Contruct an AMF Element from an ASCII string.
///
/// @param data The ASCII string to use as the value of the property.
///
/// @remarks This assume the data string is already NULL terminated.
Element::Element(const string &indata)
    : _name(0),
      _type(NOTYPE)
{
//    GNASH_REPORT_FUNCTION;
    makeString(indata);
}

/// \overload Element(const std::string &data)
Element::Element(const char *indata)
    : _name(0),
      _type(NOTYPE)
{
//    GNASH_REPORT_FUNCTION;
    makeString(indata);
}

/// \brief Contruct a Property from an ASCII string.
///
/// @param name The name of the Property
///
/// @param data The ASCII string to use as the value of the property.
Element::Element(const string &name, const string &data)
    : _name(0),
      _type(NOTYPE)
{
//    GNASH_REPORT_FUNCTION;
    makeString(name, data);
}

/// \brief Contruct an AMF Element from a Boolean
///
/// @param data The boolean to use as the value for this Element.
Element::Element(bool data)
    : _name(0),
      _type(NOTYPE)
{
//    GNASH_REPORT_FUNCTION;
    makeBoolean(data);
}

/// \brief Contruct a Property from a Boolean
///
/// @param name The name of the Property
///
/// @param data The boolean to use as the value of the property.
Element::Element(const string &name, bool indata)
    : _name(0),
      _type(NOTYPE)
{
//    GNASH_REPORT_FUNCTION;
    makeBoolean(name, indata);
}

#if 0
// Create a function block for AMF
Element::Element(bool /* flag */, double /* unknown1 */, double /* unknown2 */,
                 const string &/* methodname */)
    : _name(0),
      _type(NOTYPE),
      _referenceid(0)
{
//    GNASH_REPORT_FUNCTION;
    log_unimpl("Can't create remote function calls yet");
}

Element &
Element::init(bool flag, double unknown1, double unknown2,
              const string &methodname)
{
//    GNASH_REPORT_FUNCTION;
//    _type = Element::FUNCTION_AMF0;
    if (methodname.size()) {
        setName(methodname);
    }

    // Build up the properties for the function block
    shared_ptr<cygnal::Element> el = new Element(flag);
    _properties.push_back(el);
    
    el = new Element(unknown1);
    _properties.push_back(el);
    
    el = new Element(unknown2);
    _properties.push_back(el);
    
    el = new Element(methodname);
    _properties.push_back(el);
    
    _buffer = new Buffer(3
        + ((AMF_HEADER_SIZE + AMF0_NUMBER_SIZE) * 2)
               + methodname.size() + AMF_HEADER_SIZE);
//     memcpy(_data, &indata, _length);
    return *this;
}
#endif

/// \brief Clear the contents of the buffer by setting all the bytes to
///             zeros.
///
/// @return nothing
void
Element::clear()
{
//    GNASH_REPORT_FUNCTION;
        delete[] _name;
        _name = 0;
        _buffer.reset();
}

/// \brief Get the size in bytes of the Element's data.
///     All data in an Element is stored in a Buffer class.
///
/// @return the size in bytes.
size_t
Element::getDataSize() const
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer) {
        return _buffer->size();
    }
    return 0;
}

/// \brief Cast the data in this Element to a double value.
///
/// @return double value.
double
Element::to_number() const
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer) {
        return *(reinterpret_cast<double *>(_buffer->reference()));
    }
//    return ::nan("NaN");
    return -1.0;
}

/// \brief Cast the data in this Element to a short value.
///
/// @return short value.
boost::uint16_t
Element::to_short() const
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer) {
        return *(reinterpret_cast<boost::uint16_t *>(_buffer->reference()));
    }
//    return ::nan("NaN");
    return -1;
}

/// \brief Cast the data in this Element to a short value.
///
/// @return short value.
boost::uint32_t
Element::to_integer() const
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer) {
        return *(reinterpret_cast<boost::uint32_t *>(_buffer->reference()));
    }
//    return ::nan("NaN");
    return -1;
}

/// \brief Cast the data in this Element to an ASCII string value.
///
/// @return A NULL terminated ASCII string.
const char *
Element::to_string() const
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer) {
        if (_buffer->size() > 0) {
            return reinterpret_cast<const char *>(_buffer->reference());
        }
        return "NULL";
    }
    return 0;
}

/// \brief Cast the data in this Element to a boolean value.
///
/// @return boolean value.
bool
Element::to_bool() const
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer) {
        return *(reinterpret_cast<bool *>(_buffer->reference()));
    }
    return false;
}

/// \brief Cast the data in this Element to an real pointer to data.
///
/// @return A real pointer to the base address of the raw data in memory.
boost::uint8_t *
Element::to_reference()
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer) {
        return _buffer->reference();
    }
    return 0;
}

const boost::uint8_t *
Element::to_reference() const
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer) {
        return _buffer->reference();
    }
    return 0;
}

/// \brief Test equivalance against another Element.
///     This compares all the data and the data type in the
///     current Element with the supplied one, so it can be a
///     performance hit. This is primarily only used for
///     testing purposes.
///
/// @param buf A reference to an Element.
///
/// @return A boolean true if the Elements are indentical.
bool
Element::operator==(Element &el)
{
//    GNASH_REPORT_FUNCTION;
    int count = 0;

    // See if the names match
    if (_name) {
        if (strcmp(_name, el.getName()) == 0) {
            count++;
        }
    } else {
        if (el.getNameSize() == 0) {
            count++;
        }
    }

    // See if the types match
    if (_type == el.getType()) {
        count++;
    }

    if (_buffer && el.getDataSize()) {
        if (memcmp(_buffer->reference(), el.to_reference(), _buffer->size()) == 0) {
            count++;
        }
    } else {
        count++;
    }
    
    // FIXME: make this test more exhaustive
    if (_properties.size() == el.propertySize()) {
        count++;
    }

    if (count == 4) {
        return true;
    }
    return false;;
}

/// \brief Test equivalance against a boolean value
///     This compares all the data and the data type in the
///     current Element to see if it is a Boolean and if the
///     values ard the same. This is primarily only used for
///     testing purposes.
///
/// @param buf A boolean value
///
/// @return A boolean true if the Elements are indentical.
bool
Element::operator==(bool x)
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer) {
        *_buffer += x;
    }
    return false;
}

size_t
Element::calculateSize()
{
//    GNASH_REPORT_FUNCTION;
    return calculateSize();
}

size_t
Element::calculateSize(cygnal::Element &el) const
{
//    GNASH_REPORT_FUNCTION;    
    size_t outsize = 0;

    // Simple Elements have everything contained in just the class itself.
    // If thr name is set, it's a property, so the length is
    // prefixed to the name string.
    if (el.getNameSize()) {
        outsize += el.getNameSize() + sizeof(boost::uint16_t);
    }
    // If there is any data, then the size of the data plus the header
    // of the type and the length is next.
    if (el.getDataSize()) {
        outsize += el.getDataSize() + AMF_HEADER_SIZE;
    }

    // If an array has no data, it's undefined, so has a length of zero.
    if (el.getType() == Element::STRICT_ARRAY_AMF0) {
        if (el.getDataSize() == 0) {
            outsize = sizeof(boost::uint32_t) + 1;
        }
    }
    
    // More complex messages have child elements, either properties or
    // the items in an array, If we have children, count up their size too.
    // Calculate the total size of the message
    std::vector<boost::shared_ptr<cygnal::Element> > props = el.getProperties();
    for (size_t i=0; i<props.size(); i++) {
        outsize += props[i]->getDataSize();
        if (props[i]->getNameSize()) {
            outsize += props[i]->getNameSize();
            outsize += cygnal::AMF_PROP_HEADER_SIZE;
        } else {
            outsize += cygnal::AMF_HEADER_SIZE;
        }
    }

    return outsize;
}

/// \brief Encode this Element (data type object).
///     This encodes this Element and all of it's associated
///     properties into raw binary data in big endoan format.
///
/// @return a smart pointer to a Buffer class.
boost::shared_ptr<Buffer>
Element::encode()
{
//    GNASH_REPORT_FUNCTION;

    return encode(false);
}

boost::shared_ptr<Buffer>
Element::encode(bool notobject)
{
//    GNASH_REPORT_FUNCTION;
    size_t size = 0;
    boost::shared_ptr<Buffer> buf;

    if (_type == Element::OBJECT_AMF0) {
        // Calculate the total size of the output buffer
        // needed to hold the encoded properties
        if (_name) {
            size = getNameSize() + AMF_HEADER_SIZE;
        }
        for (size_t i=0; i<_properties.size(); i++) {
            size += _properties[i]->getDataSize();
            size += _properties[i]->getNameSize();
            size += AMF_PROP_HEADER_SIZE;
        }
        gnash::log_debug("Size of Element \"%s\" is: %d", _name, size);
        buf.reset(new Buffer(size+AMF_PROP_HEADER_SIZE));
        if (!notobject) {
            *buf = Element::OBJECT_AMF0;
        }
        if (_name > 0) {
            size_t length = getNameSize();
            boost::uint16_t enclength = length;
            swapBytes(&enclength, 2);
            *buf += enclength;
            string str = _name;
            *buf += str;
            boost::uint8_t byte = static_cast<boost::uint8_t>(0x5);
            *buf += byte;
        }

        for (size_t i=0; i<_properties.size(); i++) {
            boost::shared_ptr<Buffer> partial = AMF::encodeElement(_properties[i]);
//          log_debug("Encoded partial size for is %d", partial->size());
//          _properties[i]->dump();
//          partial->dump();
            if (partial) {
                *buf += partial;
                partial.reset();
            } else {
                break;
            }
        }
//      log_debug("FIXME: Terminating object");
        if (!notobject) {
            boost::uint8_t pad = 0;
            *buf += pad;
            *buf += pad;
            *buf += TERMINATOR;
        }
        return buf;
    } else {
            return AMF::encodeElement(*this);
    }
    
    return buf;
}

/// \brief Get the Element or Property at a specified location.
///
/// @param index The location as a numerical value of the item in
///             the array to get.
///
/// @return A smart pointer to the Element or property.
boost::shared_ptr<Element>
Element::operator[](size_t index)
{
//    GNASH_REPORT_FUNCTION;
    if (index <= _properties.size()) {
        return _properties[index];
    }
    
    boost::shared_ptr<Element> el; 
    return el;
}

/// \brief Make this Element be the same as another Element.
///
/// @param el A reference to an Element class.
///
/// @return A reference to this Element.
Element &
Element::operator=(Element &el)
{
//    GNASH_REPORT_FUNCTION;
    return operator=(&el);
//    GNASH_REPORT_FUNCTION;
    _type = el.getType();
    if (el.getNameSize()) {
        setName(el.getName());
    }
    _buffer.reset(new Buffer(el.getDataSize()));
    _buffer->copy(el.to_reference(), el.getDataSize());
    return *this;
}

/// \brief Make this Element be the same as a double.
///             This sets both the data type and the value.
///
/// @param el A double value.
///
/// @return A reference to this Element.
Element &
Element::operator=(double num)
{
    return makeNumber(num);
}

/// \brief Make this Element be the same as an ASCII string.
///             This sets both the data type and the value.
///
/// @param el An ASCII string value.
///
/// @return A reference to this Element.
Element &
Element::operator=(const string &str)
{
    return makeString(str);
}

/// \brief Make this Element be the same as a boolean value.
///             This sets both the data type and the value.
///
/// @param el A boolean value.
///
/// @return A reference to this Element.
Element &
Element::operator=(bool flag)
{
    return makeBoolean(flag);
}

/// \note All Numbers are 64 bit, big-endian (network byte order)
///     entities. All strings are in multibyte format, which is to
///     say, probably normal ASCII. It may be that these need to be
///     converted to wide characters, but for now we just leave them
///     as standard multibyte characters.

/// \brief Make this Element with an ASCII string value.
///
/// @param data The ASCII string to use as the value.
///
/// @param size The number of bytes in the ASCII string.
///
/// @return A reference to this Element.
Element &
Element::makeString(boost::uint8_t *data, size_t size)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::STRING_AMF0;

    // If there is an existing string, 
    if (_buffer) {
        if (_buffer->size() < size) {
            _buffer->resize(size+1); // add room for the NULL terminator
        }
    } else {
        // Make room for an additional NULL terminator
        try {
            check_buffer(size+1);
        } catch (std::exception& e) {
            log_error("%s", e.what());
            return *this;
        }
    }
    _buffer->clear();           // FIXME: this could be a performance issue
    _buffer->copy(data, size);
    
    // Unlike other buffers, people like to print strings, so we must add
    // a NULL terminator to the string. When encoding, we are careful to
    // to adjust the byte count down by one, as the NULL terminator doesn't
    // get written.
//     *(_buffer->end() - 1) = 0;
    _buffer->setSize(size);
    return *this;
}

/// \brief Make this Element be a NULL String type.
///     A Null String is a string with a length of zero. The
///     data is only one byte, which always has the value of
///     zero of course.
///
/// @return A reference to this Element.
Element &
Element::makeNullString()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::STRING_AMF0;
    try {
        check_buffer(sizeof(boost::uint8_t));
    } catch (std::exception& e) {
        log_error("%s", e.what());
        return *this;
    }
    
    *(_buffer->reference()) = 0;
    return *this;
}

/// \brief Make this Element with an ASCII string value.
///
/// @param str The ASCII string to use as the value.
///
/// @param size The number of bytes in the ASCII string.
///
/// @return A reference to this Element.
Element &
Element::makeString(const char *str, size_t size)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::STRING_AMF0;
    boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(const_cast<char *>(str));
    return makeString(ptr, size);
}

/// \brief Make this Element with an ASCII string value.
///
/// @param str The ASCII string to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeString(const string &str)
{
//    GNASH_REPORT_FUNCTION;
    return makeString(str.c_str(), str.size());
}

/// \brief Make this Element a Property with an ASCII String value.
///
/// @param name The name of the Property
///
/// @param str The ASCII string to use as the value of the property.
///
/// @return A reference to this Element.
Element &
Element::makeString(const string &name, const string &str)
{
//    GNASH_REPORT_FUNCTION;
    if (name.size()) {
        setName(name);
    }    
    return makeString(str.c_str(), str.size());
}

/// \brief Make this Element with a double value.
///
/// @param buf A smart pointer to a Buffer class.
///
/// @return A reference to this Element.
Element &
Element::makeNumber(boost::shared_ptr<cygnal::Buffer> buf)
{
//    GNASH_REPORT_FUNCTION;
    return makeNumber(buf->reference());
}

/// \brief Make this Element with a double value.
///             The size isn't needed as a double is always the same size.
///
/// @param str The double to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeNumber(boost::uint8_t *data)
{
//    GNASH_REPORT_FUNCTION;
    double num = *reinterpret_cast<const double*>(data);
    _type = Element::NUMBER_AMF0;
    try {
        check_buffer(AMF0_NUMBER_SIZE);
    } catch (std::exception& e) {
        log_error("%s", e.what());
        return *this;
    }
    
    *_buffer = num;
    
    return *this;
}

/// \brief Make this Element with a double value.
///
/// @param str The double to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeNumber(double num)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::NUMBER_AMF0;
    try {
        check_buffer(AMF0_NUMBER_SIZE);
    } catch (std::exception& e) {
        log_error("%s", e.what());
        return *this;
    }
    
    *_buffer = num;

    return *this;
}

/// \brief Make this Element a Property with a double value
///
/// @param name The name of the Property
///
/// @param num The double to use as the value of the property.
Element &
Element::makeNumber(const string &name, double num)
{
//    GNASH_REPORT_FUNCTION;
    if (name.size()) {
        setName(name);
    }
    return makeNumber(num);
}

/// \overload Element::makeNumber(const std::string &name, boost::uint8_t *data);
///             The size isn't needed as a double is always the same size.
Element &
Element::makeNumber(const std::string &name, boost::uint8_t *data)
{
//    GNASH_REPORT_FUNCTION;
    if (name.size()) {
        setName(name);
    }
    _type = Element::NUMBER_AMF0;
    try {
        check_buffer(AMF0_NUMBER_SIZE);
    } catch (std::exception& e) {
        log_error("%s", e.what());
        return *this;
    }
    
    *_buffer = data;
    return *this;
}

/// \brief Make this Element with a boolean value.
///
/// @param data A boolean to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeBoolean(bool flag)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::BOOLEAN_AMF0;
    try {
        check_buffer(1);
    } catch (std::exception& e) {
        log_error("%s", e.what());
        return *this;
    }
    
    *_buffer = flag;

    return *this;
}

/// \brief Make this Element a Property with a boolean value
///
/// @param name The name of the Property
///
/// @param data The boolean to use as the value of the property.
///
/// @return A reference to this Element.
Element &
Element::makeBoolean(const string &name, bool flag)
{
//    GNASH_REPORT_FUNCTION;
    if (name.size()) {
        setName(name);
    }
    return makeBoolean(flag);
}

/// \brief Make this Element with a boolean value.
///     The size isn't needed as a boolean is always the same size.
///
/// @param data A real pointer to the boolean use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeBoolean(boost::uint8_t *data)
{
//    GNASH_REPORT_FUNCTION;
    bool flag = *reinterpret_cast<const bool*>(data);
    
    return makeBoolean(flag);
}

/// \brief Make this Element an Undefined data type
///
/// @return A reference to this Element.
Element &
Element::makeUndefined()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::UNDEFINED_AMF0;

    return *this;
}

/// \brief Make this Element a Property with an Undefined data type.
///
/// @param name The name of the Property
///
/// @return A reference to this Element.
Element &
Element::makeUndefined(const std::string &name)
{
//    GNASH_REPORT_FUNCTION;
    if (name.size()) {
        setName(name);
    }
    return makeUndefined();
}

/// \brief Make this Element an NULL Object data type
///     A NULL AMF0 Object consists of a single byte, which is the type
///
/// @return A reference to this Element.
Element &
Element::makeNull()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::NULL_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an NULL Object data type.
///
/// @param name The name of the Property
///
/// @return A reference to this Element.
Element &
Element::makeNull(const std::string &name)
{
//    GNASH_REPORT_FUNCTION;    
    if (name.size()) {
        setName(name);
    }
    return makeNull();
}

/// \brief Make this Element as a Object data type.
///     This is AMF data type that supports complex objects
///     with properties. A Reference refers to a previously
///     sent ActionScript object to save on bandwidth.
///
/// @param data A smart pointer to an Element to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeObject()
{
//    GNASH_REPORT_FUNCTION;
    _type = OBJECT_AMF0;
    return *this;
}

/// \brief Make this Element as an Object data type.
///
/// @param name The name of this object. This is not the same as
///             the name of a property.
///
/// @return A reference to this Element.
Element &
Element::makeObject(const std::string &name)
{
//    GNASH_REPORT_FUNCTION;
    if (name.size()) {
        setName(name);
    }
    return makeObject();
}

/// \brief Make this Element a Property with an Object as the value.
///
/// @param name The name of the Property
///
/// @param data A smart pointer to an Element to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeObject(const std::string &name, std::vector<boost::shared_ptr<Element> > &data)
{
//    GNASH_REPORT_FUNCTION;
    _type = OBJECT_AMF0;
    if (name.size()) {
        setName(name);
    }
    return makeObject(data);
}

/// \brief Make this Element as an Object data type.
///
/// @param data A smart pointer to an Element to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeObject(std::vector<boost::shared_ptr<Element> > &data)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::OBJECT_AMF0;
    std::vector<boost::shared_ptr<Element> >::const_iterator ait;
    for (ait = data.begin(); ait != data.end(); ait++) {
        boost::shared_ptr<Element> el = (*(ait));
        addProperty(el);
//      el->dump(os);
    }
    return *this;
}

/// \brief Make this Element as an XML Object data type.
///     This is like a string object, but the type is different.
///
/// @return A reference to this Element.
Element &
Element::makeXMLObject()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::XML_OBJECT_AMF0;
    return *this;
}
Element &
Element::makeXMLObject(boost::uint8_t * /*data*/)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::XML_OBJECT_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an XML Object as the value.
///
/// @param name The name of the Property
///
/// @param data The boolean to use as the value of the property.
///
/// @return A reference to this Element.
Element &
Element::makeXMLObject(const string &data)
{
//    GNASH_REPORT_FUNCTION;
    makeString(data);
    _type = Element::XML_OBJECT_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an XML Object as the value.
///
/// @param name The name of the Property
///
/// @param data A smart pointer to an Element to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeXMLObject(const string &name, const string &data)
{
//    GNASH_REPORT_FUNCTION;
    makeXMLObject(name, data);
    _type = Element::XML_OBJECT_AMF0;
    return *this;
}

/// \brief Make this Element an Object End data type
///
/// @return A reference to this Element.
Element &
Element::makeObjectEnd()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::OBJECT_END_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an Typed Object as the value.
///
/// @param name The name of the Property
///
/// @return A reference to this Element.    
Element &
Element::makeTypedObject(const std::string &name)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::TYPED_OBJECT_AMF0;  
    if (name.size()) {
        setName(name);
    }
    return *this;
}

Element &
Element::makeTypedObject()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::TYPED_OBJECT_AMF0;  

    return *this;
}

/// \brief Make this Element a Property with an Typed Object as the value.
///
/// @param data A real pointer to the raw data to use as the value.
///
/// @param size The number of bytes to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeTypedObject(boost::uint8_t */*data*/)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::TYPED_OBJECT_AMF0;
//     check_buffer(size);
//     _buffer->copy(data, size);
    return *this;
}

/// \brief Make this Element a Property with an Object Reference as the value.
///
/// @return A reference to this Element.
Element &
Element::makeReference()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::REFERENCE_AMF0;
    return *this;
}

Element &
Element::makeReference(boost::uint16_t index)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::REFERENCE_AMF0;
    boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(&index);
    return makeReference(ptr, sizeof(boost::uint16_t));
    
    return *this;
}

/// \brief Make this Element a Property with an Object Reference as the value.
///
/// @param data A real pointer to the raw data to use as the value.
///
/// @param size The number of bytes to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeReference(boost::uint8_t *indata, size_t size)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::REFERENCE_AMF0;
    try {
        check_buffer(size);
    } catch (std::exception& e) {
        log_error("%s", e.what());
        return *this;
    }
    
    _buffer->copy(indata, size);
    return *this;
}

/// \brief Make this Element a Property with a Movie Clip (SWF data) as the value.
///
/// @return A reference to this Element.
Element &
Element::makeMovieClip()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::MOVIECLIP_AMF0;
    return *this;
}

/// \brief Make this Element a Property with a Movie Clip (SWF data) as the value.
///
/// @param data A real pointer to the raw data to use as the value.
///
/// @param size The number of bytes to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeMovieClip(boost::uint8_t *indata, size_t size)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::MOVIECLIP_AMF0;
    check_buffer(size);
    _buffer->copy(indata, size);
    return *this;    
}

/// \brief Make this Element a Property with an ECMA Array as the value.
///             This is a mixed array of any AMF types. These are stored
///             the same as an object, but with a different type.
///
/// @return A reference to this Element.
Element &
Element::makeECMAArray()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::ECMA_ARRAY_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an ECMA Array as the value.
///
/// @param name The name of the Property
///
/// @return A reference to this Element.
Element &
Element::makeECMAArray(const std::string &name)
{
//    GNASH_REPORT_FUNCTION;
    if (name.size()) {
        setName(name);
    }
    return makeECMAArray();
}

/// \brief Make this Element a Property with an ECMA Array as the value.
///
/// @param name The name of the Property
///
/// @param data A smart pointer to a vector of Elements to use as the vaule.
///
/// @return A reference to this Element.
Element &
Element::makeECMAArray(const std::string &name, std::vector<boost::shared_ptr<cygnal::Element> > &data)
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::ECMA_ARRAY_AMF0;
    makeObject(name, data);
    _type = Element::ECMA_ARRAY_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an ECMA Array as the value.
///     This is a mixed array of any AMF types. These are stored
///     the same as an object, but with a different type.
///
/// @param data A smart pointer to a vector of Elements to use as the vaule.
///
/// @return A reference to this Element.
Element &
Element::makeECMAArray(std::vector<boost::shared_ptr<cygnal::Element> > &data)
{
//    GNASH_REPORT_FUNCTION;
    makeObject(data);
    _type = Element::ECMA_ARRAY_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an Strict Array as the value.
///     This is an array of a single AMF type. These are stored
///     the same as an object, but with a different type.
///
/// @return A reference to this Element.
Element &
Element::makeStrictArray()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::STRICT_ARRAY_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an Strict Array as the value.
///             This is an array of a single AMF type. These are stored
///             the same as an object, but with a different type.
///
/// @param name The name of the Property
///
/// @return A reference to this Element.
Element &
Element::makeStrictArray(const std::string &name)
{
//    GNASH_REPORT_FUNCTION;
    if (name.size()) {
        setName(name);
    }
    return makeStrictArray();
}

/// \brief Make this Element a Property with an Strict Array as the value.
///
/// @param name The name of the Property
///
/// @param data A smart pointer to a vector of Elements to use as the vaule.
///
/// @return A reference to this Element.
Element &
Element::makeStrictArray(const std::string &name, std::vector<boost::shared_ptr<cygnal::Element> > &data)
{
//    GNASH_REPORT_FUNCTION;
    makeObject(name, data);
    _type = Element::STRICT_ARRAY_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an ECMA Array as the value.
///     This is an array of a single AMF type. These are stored
///     the same as an object, but with a different type.
///
/// @param data A smart pointer to a vector of Elements to use as the vaule.
///
/// @return A reference to this Element.
Element &
Element::makeStrictArray(std::vector<boost::shared_ptr<cygnal::Element> > &data)
{
//    GNASH_REPORT_FUNCTION;
    makeObject(data);
    _type = Element::STRICT_ARRAY_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an Unsupported value.
///
/// @return A reference to this Element.
Element &
Element::makeUnsupported()
{
//    GNASH_REPORT_FUNCTION;    
    _type = Element::UNSUPPORTED_AMF0;
    return *this;
}

/// \brief Make this Element a Property with an Unsupported value.
///
/// @param data A real pointer to the raw data to use as the value.
///
/// @param size The number of bytes to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeUnsupported(boost::uint8_t *data)
{
    UNUSED(data);
    _type = Element::UNSUPPORTED_AMF0;
    return *this;
}

/// \brief Make this Element a Property with a UTF8 String as the value.
///
/// @return A reference to this Element.
Element &
Element::makeLongString()
{
//    GNASH_REPORT_FUNCTION;    
    _type = Element::LONG_STRING_AMF0;
    return *this;
}

/// \brief Make this Element a Property with a UTF8 String as the value.
///
/// @param data A real pointer to the raw data to use as the value.
///
/// @param size The number of bytes to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeLongString(boost::uint8_t *indata)
{
    UNUSED(indata);
    _type = Element::LONG_STRING_AMF0;
//     check_buffer(size);
//     _buffer->copy(indata, size);
    return *this;
}

/// \brief Make this Element a Property with a Record Set as the value.
///
/// @return A reference to this Element.
Element &
Element::makeRecordSet()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::RECORD_SET_AMF0;
    return *this;
}
Element &
Element::makeRecordSet(boost::uint8_t *data)
{
    UNUSED(data);
    _type = Element::RECORD_SET_AMF0;
    return *this;
}

/// \brief Make this Element a Property with a Date as the value.
///
/// @param data A real pointer to the raw data to use as the value.
///
/// @return A reference to this Element.
Element &
Element::makeDate()
{
//    GNASH_REPORT_FUNCTION;
    _type = Element::DATE_AMF0;

    return *this;
}

Element &
Element::makeDate(boost::uint8_t *date)
{
//    GNASH_REPORT_FUNCTION;

    makeNumber(date);
    _type = Element::DATE_AMF0;

    return *this;
}

Element &
Element::makeDate(double date)
{
//    GNASH_REPORT_FUNCTION;
    //boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(&date);
    _type = Element::DATE_AMF0;
    try {
        check_buffer(AMF0_NUMBER_SIZE);
    } catch (std::exception& e) {
        log_error("%s", e.what());
        return *this;
    }
    
    *_buffer = date;

    return *this;
}

/// \brief Get the number of bytes in the name of this Element.
///     Only top level Objects or properties have a name.
///
/// @return The size of the name string.
size_t
Element::getNameSize() const
{
//    GNASH_REPORT_FUNCTION;
    if (_name) {
        return strlen(_name);
    }
    return 0;
}

/// \brief Set the name of this Element or property.
///             Only top level Objects or properties have a name.
///
/// @param str the name to use for this Element.
/// 
/// @return nothing.
void
Element::setName(const string &str)
{
//    GNASH_REPORT_FUNCTION;
    _name = new char[str.size() + 1];
    std::copy(str.begin(), str.end(), _name);
    *(_name + str.size()) = 0;
}

/// \brief Set the name of this Element or property.
///             Only top level Objects or properties have a name.
///
/// @param name A real pointer to the raw bytes to use as the name for this Element.
///
/// @param size The number of bytes to use for the name.
///
/// @return nothing.
///
/// @remarks This adds a NULL string terminator so the name can be printed.
void
Element::setName(const char *name, size_t size)
{
//    GNASH_REPORT_FUNCTION;
    boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(const_cast<char *>(name));
    return setName(ptr, size);
}

/// \brief Set the name of this Element or property.
///             Only top level Objects or properties have a name.
///
/// @param name A real pointer to the raw bytes to use as the name for this Element.
///
/// @param size The number of bytes to use for the name.
///
/// @return nothing.
///
/// @remarks This adds a NULL string terminator so the name can be printed.
void
Element::setName(boost::uint8_t *name, size_t size)
{
//    GNASH_REPORT_FUNCTION;
    if ((size > 0) && (name != 0)) {
        _name = new char[size+1];
        std::copy(name, name+size, _name);
        *(_name + size) = 0;
    }
}

/// \brief Make sure the Buffer used for storing data is big enough.
///             This will force an exception if the Buffer used to
//              store the data isn't big enough to hold the new size.
///
/// @param size The minimum size the buffer needs to be.
///
/// @return nothing
void
Element::check_buffer(size_t size)
{
//    GNASH_REPORT_FUNCTION;
    if (_buffer == 0) {
        _buffer.reset(new Buffer(size));
    } else {
        if (_buffer->size() < size) {
            throw gnash::ParserException("Buffer not big enough, try resizing!");
        }
        if (_buffer->size() == 0) {
            throw gnash::ParserException("Buffer has zero size, not initialized!");
        }
    }
}

///  \brief Dump the internal data of this class in a human readable form.
/// @remarks This should only be used for debugging purposes.
void
Element::dump(std::ostream& os) const
{
//    GNASH_REPORT_FUNCTION;
    
    os << astype_str[_type] << ": ";
    if (_name) {
        os << " property name is: \"" << _name << "\", ";
    } else {
        os << "(no name), ";
    }
    os << "data length is " << getDataSize() << std::endl;


    switch (_type) {
      case Element::NUMBER_AMF0:
          os << to_number() << std::endl;
          break;
      case Element::BOOLEAN_AMF0:
          os << (to_bool() ? "true" : "false") << std::endl;
          break;
      case Element::STRING_AMF0:
          os << "(" << getDataSize() << " bytes): ";
          if (getDataSize()) {
              os << "\t\"" << to_string() << "\"";
          }
          std::cerr << std::endl;
          break;
      case Element::OBJECT_AMF0:
          break;
      case Element::MOVIECLIP_AMF0:
      case Element::NULL_AMF0: 
      case Element::UNDEFINED_AMF0:
      case Element::REFERENCE_AMF0:
      case Element::ECMA_ARRAY_AMF0:
      case Element::OBJECT_END_AMF0:
      case Element::STRICT_ARRAY_AMF0:
      case Element::DATE_AMF0:
      case Element::LONG_STRING_AMF0:
      case Element::UNSUPPORTED_AMF0:
      case Element::RECORD_SET_AMF0:
      case Element::XML_OBJECT_AMF0:
      case Element::TYPED_OBJECT_AMF0:
          std::cerr << std::endl;
          break;
      case Element::AMF3_DATA:
          if (getDataSize() != 0) {
              gnash::log_debug("FIXME: got AMF3 data!");
          }
//        cerr << "AMF3 data is: 0x" << hexify(_data, _length, false) << endl;
          break;
//       case Element::VARIABLE:
//       case Element::FUNCTION:
//        os << "# of properties in object: " << properties.size() << endl;
//        for (size_t i=0; i< properties.size(); i++) {
//            properties[i]->dump();
//        }
//        break;
      default:
//        log_unimpl("%s: type %d", __PRETTY_FUNCTION__, (int)_type);
          break;
    }

    if (_type != Element::BOOLEAN_AMF0) {
        if (_buffer) {
            _buffer->dump();
        }
    }

    if (_properties.size() > 0) {
        std::vector<boost::shared_ptr<Element> >::const_iterator ait;
        os << "# of Properties in object: " << _properties.size() << std::endl;
        for (ait = _properties.begin(); ait != _properties.end(); ait++) {
            const boost::shared_ptr<Element> el = (*(ait));
            el->dump(os);
        }
    }
}

/// \brief Find the named property for this Object.
///
/// @param name An ASCII string that is the name of the property to
///     search for.
///
/// @return A smart pointer to the Element for this property.
boost::shared_ptr<cygnal::Element> 
Element::findProperty(const std::string &name)
{
    if (_properties.size() > 0) {
        std::vector<boost::shared_ptr<Element> >::iterator ait;
//      cerr << "# of Properties in object: " << _properties.size() << endl;
        for (ait = _properties.begin(); ait != _properties.end(); ait++) {
            boost::shared_ptr<Element> el = (*(ait));
            if (el->getName() == name) {
                return el;
            }
//          el->dump();
        }
    }
    boost::shared_ptr<Element> el;
    return el;
}

} // end of amf namespace

// local Variables:
// mode: C++
// indent-tabs-mode: t
// End:

/* [<][>][^][v][top][bottom][index][help] */