/* 
 * © 2007-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "stdafx.h"

#include "grtpp.h"
#include "grtpp_util.h"
#include <glib.h>
#include <algorithm>

#ifdef _WIN32
#include <hash_set>
typedef stdext::hash_set<std::string> string_hash_set;
#else
#include <ext/hash_set>

struct string_hash : public std::unary_function<std::string, size_t> 
{
  size_t operator() (const std::string &v) const
  {
    return __gnu_cxx::hash<const char*>()(v.c_str());
  }
};

typedef __gnu_cxx::hash_set<std::string, string_hash> string_hash_set;
#endif

using namespace grt;


inline std::string get_prop(xmlNodePtr node, const char *name)
{
  xmlChar *prop= xmlGetProp(node, (xmlChar*)name);
  std::string tmp= prop ? (char*)prop : "";
  xmlFree(prop);
  return tmp;
}


inline bool get_type_spec(xmlNodePtr node, TypeSpec &type, bool allow_void= false)
{
  std::string s= get_prop(node, "type");
  
  if (allow_void && s == "void")
  {
    type.base.type= UnknownType;
    return true;
  }

  type.base.type= str_to_type(s);
  if (type.base.type == UnknownType)
  {
    g_warning("[XML parser] Unknown type '%s'.", s.c_str());
    return false;
  }
  
  // lists and dicts can be typed
  if ((type.base.type == ListType) || (type.base.type == DictType))
  {
    std::string content_type= get_prop(node, "content-type");
    std::string content_object_class= get_prop(node, "content-struct-name");
    
    if (!content_type.empty())
    {
      type.content.type= str_to_type(content_type);
      if (type.content.type == UnknownType)
      {
        g_warning("[XML parser] Unknown content-type '%s'.", content_type.c_str());
        return false;
      }
    }
    if (!content_object_class.empty())
      type.content.object_class= content_object_class;
  }
  else if (type.base.type == ObjectType)
  {
    std::string class_name= get_prop(node, "struct-name");
    
    if (!class_name.empty())
      type.base.object_class= class_name;
    else
    {
      g_warning("[XML parser] object member without struct-name.");
      return false;
    }
  }
  return true;
}


/** Generates the checksum of a GRT metaclass.
 *
 *   Calculates the checksum of all members+types of the checksum so that
 * changes made to them, could be caught by comparing checksums. Both
 * the names and types of members are checksummed. The order of members does
 * not affect the checksum.
 * 
 * Note: crc32 from Public Domain code from www.varnish-cache.org
 * 
 * @param metaclass the class to be checksummed
 *
 * @return 32bit CRC of the class.
 */
static unsigned int make_checksum(MetaClass *metaclass)
{
  static unsigned int crc32bits[] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
  };
  
  const unsigned char *p;
  unsigned int crc= ~0U;
  GSList *strings= NULL;
  GSList *node;
  
  const std::map<std::string,MetaClass::Member> &members(metaclass->get_members_partial());
  const std::map<std::string,MetaClass::Method> &methods(metaclass->get_methods_partial());
  
  // create a sorted list with the class members  
  for (std::map<std::string,MetaClass::Member>::const_iterator iter= members.begin(); 
       iter != members.end(); ++iter)
  {
    const MetaClass::Member &member(iter->second);
    
    strings= g_slist_insert_sorted(strings,
                                   g_strdup_printf("%s %i %s %i %s %i %i %i %i %i",
                                                   member.name.c_str(),
                                                   member.type.base.type,
                                                   member.type.base.object_class.c_str(),
                                                   member.type.content.type,
                                                   member.type.content.object_class.c_str(),
                                                   member.read_only?1:0,
                                                   member.delegate_get?1:0,
                                                   member.delegate_set?1:0,
                                                   member.private_?1:0,
                                                   member.calculated?1:0),
                                   (GCompareFunc)strcmp);
  }
  for (std::map<std::string,MetaClass::Method>::const_iterator iter= methods.begin(); 
       iter != methods.end(); ++iter)
  {
    const MetaClass::Method &method(iter->second);
    char *tmp= g_strdup_printf("%s %i %s %i %s",
                               method.name.c_str(),
                               method.ret_type.base.type,
                               method.ret_type.base.object_class.c_str(),
                               method.ret_type.content.type,
                               method.ret_type.content.object_class.c_str());
    strings= g_slist_insert_sorted(strings, tmp, (GCompareFunc)strcmp);
    
    for (ArgSpecList::const_iterator iter= method.arg_types.begin();
         iter != method.arg_types.end(); ++iter)
    {
      char *tmp= g_strdup_printf("= %s %i %s %i %s",
                                 iter->name.c_str(),
                                 iter->type.base.type,
                                 iter->type.base.object_class.c_str(),
                                 iter->type.content.type,
                                 iter->type.content.object_class.c_str());
      
      strings= g_slist_insert_sorted(strings, tmp, (GCompareFunc)strcmp);
    }
  }
  
  strings= g_slist_prepend(strings, 
                           g_strdup_printf("%s : %s", 
                                           metaclass->name().c_str(), 
                                           metaclass->parent() ? metaclass->parent()->name().c_str(): ""));
  
  // calculate crc of everything
  for (node= strings; node != NULL; node= g_slist_next(node))
  {
    for (p = (const unsigned char*)node->data; *p != 0; p++)
      crc = (crc >> 8) ^ crc32bits[(crc ^ *p) & 0xff];
    
    g_free(node->data);
  }
  
  // free everything
  g_slist_free(strings);
  
  return (crc ^ ~0U);
}


bool MetaClass::foreach_member(const sigc::slot<bool, const Member*> &slot)
{
  // set of already seen members (only overriden ones)
  string_hash_set seen;
  MetaClass *mc= this;

  do
  {
    for (MemberList::const_iterator mem= mc->_members.begin(); mem != mc->_members.end(); ++mem)
    {
      if (seen.find(mem->first) != seen.end())
        continue;
      seen.insert(mem->first);

      if (!slot(&mem->second))
        return false;
    }
    mc= mc->_parent;
  }
  while (mc != 0);

  return true;
}


bool MetaClass::foreach_method(const sigc::slot<bool, const Method*> &slot)
{
  // set of already seen methods (only overriden ones)
  string_hash_set seen;
  MetaClass *mc= this;

  do
  {
    for (MethodList::const_iterator mem= mc->_methods.begin(); mem != mc->_methods.end(); ++mem)
    {
      if (seen.find(mem->first) != seen.end())
        continue;
      seen.insert(mem->first);

      if (!slot(&mem->second))
        return false;
    }
    mc= mc->_parent;
  }
  while (mc != 0);

  return true;
}


bool MetaClass::foreach_signal(const sigc::slot<bool, const Signal*> &slot)
{
  // set of already seen methods (only overriden ones)
  string_hash_set seen;
  MetaClass *mc= this;

  do
  {
    for (SignalList::const_iterator mem= mc->_signals.begin(); mem != mc->_signals.end(); ++mem)
    {
      if (seen.find(mem->name) != seen.end())
        continue;
      seen.insert(mem->name);

      if (!slot(&*mem))
        return false;
    }
    mc= mc->_parent;
  }
  while (mc != 0);

  return true;
}


bool MetaClass::has_member(const std::string &member) const
{
  if (_members.find(member) == _members.end())
  {
    if (_parent)
      return _parent->has_member(member);
    return false;
  }
  return true;
}


bool MetaClass::has_method(const std::string &method) const
{
  if (_methods.find(method) == _methods.end())
  {
    if (_parent)
      return _parent->has_method(method);
    return false;
  }
  return true;
}


bool MetaClass::is_a(const std::string &name) const
{
  MetaClass *mc= _grt->get_metaclass(name);
  if (!mc)
    return false;

  return is_a(mc);
}

bool MetaClass::is_a(MetaClass *struc) const
{
  const MetaClass *par= this;
  
  while (par && par != struc)
    par= par->_parent;
  
  return par == struc;
}


MetaClass::MetaClass(GRT *grt)
: _grt(grt)
{
  _crc32= 0;
  _parent= 0;
  _placeholder= false;
  _alloc= 0;
  _bound= false;

  _impl_data= false;
  _force_impl= false;
  _watch_lists= false;
  _watch_dicts= false;
}


MetaClass::~MetaClass()
{
  for (MemberList::iterator iter= _members.begin(); iter != _members.end(); ++iter)
    delete iter->second.property;
}


MetaClass* MetaClass::create_base_class(GRT *grt)
{
  MetaClass *mc= new MetaClass(grt);
  mc->_name= internal::Object::static_class_name();
  mc->_placeholder= false;
  mc->bind_allocator(0);
  return mc;
}

MetaClass* MetaClass::from_xml(GRT *grt, const std::string &source, xmlNodePtr node)
{
  std::string name= get_prop(node, "name");
  MetaClass *stru;
  
  if (!name.empty())
  {
    stru= grt->get_metaclass(name);
    if (stru)
    {
      if (!stru->_placeholder)
        throw std::runtime_error(std::string("Error loading struct from ").append(source).append(": duplicate struct name ").append(name));
      stru->_placeholder= false;
    }
    else
      stru= new MetaClass(grt);
  }
  else
    throw std::runtime_error("Invalid struct definition in "+source);

  stru->_source= source;
  stru->load_xml(node);
  
  return stru;
}


void MetaClass::load_attribute_list(xmlNodePtr node, const std::string &member)
{
  if (node->properties)
  {
    xmlAttr *attr= node->properties;
    std::string prefix;
    
    prefix.append(member);
    if (!prefix.empty())
      prefix.append(":");

    while (attr)
    {
      if (attr->ns && xmlStrcmp(attr->ns->prefix, (xmlChar*)"attr")==0)
      {
        xmlChar *prop= xmlGetNsProp(node, attr->name, attr->ns->href);
        _attributes[prefix+(char*)attr->name]= (char*)prop;
        xmlFree(prop);
      }
      attr= attr->next;
    }
  }  
}


void MetaClass::load_xml(xmlNodePtr node)
{
  std::string node_property= get_prop(node, "name");
  xmlNodePtr child_node;
  
  if (xmlStrcmp(node->name, (xmlChar*)"gstruct") != 0)
  {
    g_warning("[XML parser] Node '%s': 'gstruct' expected.", node->name);
    throw std::runtime_error("missing 'metaclass' loading grt xml");
  }
  
  if (node_property.empty())
  {
    g_warning("[XML parser] Node '%s' does not have a name property.", node->name);
    throw std::runtime_error("missing 'name' loading grt xml");
  }

  _name= node_property;

  if (get_prop(node, "force-impl") == "1")
    _force_impl= true;

  if (get_prop(node, "watch-lists") == "1")
    _watch_lists= true;

  if (get_prop(node, "watch-dicts") == "1")
    _watch_dicts= true;

  if (get_prop(node, "impl-data") == "1")
    _impl_data= true;

  node_property= get_prop(node, "parent");
  if (node_property.empty())
    node_property= internal::Object::static_class_name();

  MetaClass *parent= _grt->get_metaclass(node_property);
  if (parent)
    _parent= parent;
  else
  {
    // if the parent is not loaded yet, create a placeholder object to be filled later
    MetaClass *tmp= new MetaClass(_grt);
    tmp->_name= node_property;
    tmp->_source= _source;
    tmp->_placeholder= true;
    _parent= tmp;
    _grt->add_metaclass(tmp);
  }

  load_attribute_list(node);

  // read in members and child_structs
  child_node= node->children;
  while (child_node)
  {
    // members
    if (xmlStrcmp(child_node->name, (xmlChar*)"members")==0)
    {
      xmlNodePtr member_node= child_node->children;
      
      while (member_node)
      {
        if (xmlStrcmp(member_node->name, (xmlChar*)"member")==0)
        {
          Member member;
          member.read_only= false;
          member.delegate_get= false;
          member.delegate_set= false;
          member.private_= false;
          member.calculated= false;
          member.owned_object= false;
          member.overrides= false;
          member.null_content_allowed= true;

          member.property= 0;

          std::string type= get_prop(member_node, "type");

          member.name= get_prop(member_node, "name");          
          member.default_value= get_prop(member_node, "default");

          if (!get_prop(member_node, "dontfollow").empty())
            g_warning("[XML parser] Node '%s' contains 'attr:dontfollow' prop which was replaced with 'owned'.", _name.c_str());
          
          if (get_prop(member_node, "read-only") == "1")
            member.read_only= true;
          else
            member.read_only= false;
          if (get_prop(member_node, "private") == "1")
            member.private_= true;
          else
            member.private_= false;
          if (get_prop(member_node, "delegate-get") == "1")
            member.delegate_get= true;
          else
            member.delegate_get= false;
          if (get_prop(member_node, "delegate-set") == "1")
            member.delegate_set= true;
          else
            member.delegate_set= false;
          if (get_prop(member_node, "calculated") == "1")
            member.calculated= true;
          else
            member.calculated= false;
          if (get_prop(member_node, "owned") == "1")
            member.owned_object= true;
          else
            member.owned_object= false;
          if (get_prop(member_node, "overrides") != "" && get_prop(member_node, "overrides") != "0")
            member.overrides= true;
          else
            member.overrides= false;
          if (get_prop(member_node, "allow-null") == "1")
            member.null_content_allowed= true;
          else
            member.null_content_allowed= false;
          
          load_attribute_list(member_node, member.name);

          // don't override the whole member, only the attributes
          if (get_prop(member_node, "override-attributes-only") != "1")
          {
            if (!get_type_spec(member_node, member.type))
              g_warning("[XML parser] Node '%s'::'%s' contains invalid type specification.",
                      _name.c_str(), member.name.c_str());
            
            // do some validation
            if (!is_container_type(member.type.base.type) && member.owned_object)
              g_warning("[XML parser] Node '%s'::'%s' marked as 'owned', but is not an object.",
                        _name.c_str(), member.name.c_str());
            
            if (member.calculated && (!member.delegate_get || (!member.read_only && !member.delegate_set)))
              g_warning("[XML parser] Node '%s'::'%s' marked as 'calculated', but accessors are not delegated.",
                        _name.c_str(), member.name.c_str());
            
            if (member.calculated && member.private_)
              g_warning("[XML parser] Node '%s'::'%s' marked as 'private' and 'calculated', which is not allowed.",
                        _name.c_str(), member.name.c_str());
            
            if (member.calculated && member.owned_object)
              g_warning("[XML parser] Node '%s'::'%s' marked as 'owned' and 'calculated', which is not allowed.",
                        _name.c_str(), member.name.c_str());
            
            // can't replace lists/dicts members in objects
            if ((member.type.base.type == ListType) || (member.type.base.type == DictType))
              member.read_only= true;
            
            _members[member.name]= member;
          }
        }
        else if (xmlStrcmp(member_node->name, (xmlChar*)"method")==0
                 || xmlStrcmp(member_node->name, (xmlChar*)"constructor")==0)
        {
          Method method;

          method.constructor= xmlStrcmp(member_node->name, (xmlChar*)"constructor")==0;
          method.abstract= false;

          method.name= get_prop(member_node, "name");

          if (get_prop(member_node, "constructor") == "1")
            method.constructor= true;
          if (get_prop(member_node, "abstract") == "1")
            method.abstract= true;

          if (!method.constructor && !get_type_spec(member_node, method.ret_type, true))
            g_warning("[XML parser] Node '%s'::'%s' contains invalid type specification.",
                      _name.c_str(), method.name.c_str());

          if (method.constructor && method.abstract)
            g_warning("[XML parser] Node '%s'::'%s' cannot be both abstract and constructor.",
                      _name.c_str(), method.name.c_str());


          xmlNodePtr arg_node= member_node->children;
          while (arg_node)
          {
            if (xmlStrcmp(arg_node->name, (xmlChar*)"argument")==0)
            {
              ArgSpec arg;
            
              arg.name= get_prop(arg_node, "name");
              if (!get_type_spec(arg_node, arg.type))
                g_warning("[XML parser] Node '%s'::'%s'::'%s' contains invalid argument type specification",
                          _name.c_str(), method.name.c_str(), arg.name.c_str());

              method.arg_types.push_back(arg);
            }
            arg_node= arg_node->next;
          }

          _methods[method.name]= method;
        }
        else if (xmlStrcmp(member_node->name, (xmlChar*)"signal")==0)
        {
          Signal sig;

          sig.name= get_prop(member_node, "name");

          xmlNodePtr arg_node= member_node->children;
          while (arg_node)
          {
            if (xmlStrcmp(arg_node->name, (xmlChar*)"argument")==0)
            {
              SignalArg arg;
              std::string type= get_prop(arg_node, "type");
              
              if (type == "bool")
                arg.type= BoolSArg;
              else if (type == "int")
                arg.type= IntSArg;
              else if (type == "double")
                arg.type= DoubleSArg;
              else if (type == "string")
                arg.type= StringSArg;
              else if (type == "object")
              {
                arg.type= ObjectSArg;
                arg.object_class= get_prop(arg_node, "struct-name");
              }
              else
                g_warning("Signal '%s'::'%s' contains invalid argument type '%s'",
                          _name.c_str(), sig.name.c_str(), type.c_str());

              sig.arg_types.push_back(arg);
            }
            arg_node= arg_node->next;
          }

          _signals.push_back(sig);
        }
        member_node= member_node->next;
      }
    }
    
    child_node= child_node->next;
  }

  _crc32= make_checksum(this);
}


bool MetaClass::is_bound() const
{
  return _bound;
}


bool MetaClass::validate()
{
  std::map<std::string, std::string> seen;
  bool ok= true;
  
  for (MemberList::const_iterator mem= _members.begin(); mem != _members.end(); ++mem)
  {
    const Member *member;
    
    // check if the member is overriding another one
    if (_parent && (member= _parent->get_member_info(mem->second.name)))
    {      
      if (member->type.base.type != mem->second.type.base.type)
      {
        g_warning("Member %s::%s overrides a member with a different base type",
                  _name.c_str(), mem->second.name.c_str());
        ok= false;
      }
      else
      {
        // check if the overriding type is compatible
        switch (member->type.base.type)
        {
        case ListType:
        case DictType:
          if (member->type.content.type != mem->second.type.content.type)
          {
            g_warning("Member %s::%s overrides a member with a different content type",
                      _name.c_str(), mem->second.name.c_str());
            ok= false;
          }
          if (member->type.content.type == ObjectType)
          {
            MetaClass *member_content_class1;
            MetaClass *member_content_class2;

            if (!(member_content_class1= _grt->get_metaclass(mem->second.type.content.object_class)))
            {
              g_warning("Member %s::%s has invalid content object class '%s'",
                        _name.c_str(), mem->second.name.c_str(),
                        mem->second.type.content.object_class.c_str());
              ok= false;
            }

            member_content_class2= _grt->get_metaclass(member->type.content.object_class);
            if (member_content_class1 && member_content_class2)
            {
              if (!member_content_class1->is_a(member_content_class2))
              {
                g_warning("Member %s::%s overrides a member with an incompatible content object class",
                          _name.c_str(), mem->second.name.c_str());
                ok= false;
              }
            }
          }
          break;
        case ObjectType:
          if (member->type.content.object_class != mem->second.type.content.object_class)
          {
            g_warning("Member %s::%s overrides a member with a different class",
                      _name.c_str(), mem->second.name.c_str());
            ok= false;
          }
          break;
        default:
          break;
        }
      }
      if (ok)
      {
        // mark it as overrides
        _members[mem->first].overrides= true;
      }
    }
    
    if (seen.find(mem->second.name) != seen.end() && !mem->second.overrides)
    {
      g_warning("Member %s::%s is duplicate", 
                _name.c_str(), mem->second.name.c_str());
      ok= false;
    }
    seen[mem->second.name]= _name;
  }

  return ok;
}


std::string MetaClass::get_attribute(const std::string &attr)
{
  std::map<std::string,std::string>::const_iterator iter= _attributes.find(attr);
  if (iter == _attributes.end())
  {
    if (_parent)
      return _parent->get_attribute(attr);
    return "";
  }
  return iter->second;
}


std::string MetaClass::get_member_attribute(const std::string &member, const std::string &attr)
{
  std::map<std::string,std::string>::const_iterator iter= _attributes.find(member+":"+attr);
  if (iter == _attributes.end())
  {
    if (_parent)
      return _parent->get_member_attribute(member, attr);
    return "";
  }
  return iter->second;
}


bool MetaClass::is_abstract() const
{
  if (_bound && !_alloc)
    return true;

  for (MethodList::const_iterator iter= _methods.begin(); iter != _methods.end(); ++iter)
  {
    if (iter->second.abstract)
      return true;
  }

  return false;
}


ObjectRef MetaClass::allocate()
{
  if (is_abstract())
    throw std::runtime_error("cannot allocate an abstract class");

  if (!_bound)
    throw std::runtime_error("GRT class "+name()+" was not initialized/registered with the GRT instance");

  ObjectRef object= (*_alloc)(_grt);
  object->init();

  return object;
}


void MetaClass::bind_allocator(Allocator alloc)
{
  _alloc= alloc;
  _bound= true;
}


void MetaClass::bind_member(const std::string &name, PropertyBase *prop)
{
  std::map<std::string,Member>::iterator iter= _members.find(name);
  if (iter == _members.end())
    throw std::runtime_error("Attempt to bind invalid member "+name);
  
  iter->second.property= prop;
}


void MetaClass::bind_method(const std::string &name, Method::Function method)
{
  std::map<std::string,Method>::iterator iter= _methods.find(name);
  if (iter == _methods.end())
    throw std::runtime_error("Attempt to bind invalid method "+name);
 
  iter->second.function= method;
}


void MetaClass::set_member_value(internal::Object *object, const std::string &name, const ValueRef &value)
{
  set_member_internal(object, name, value, false);
}


void MetaClass::set_member_internal(internal::Object *object, const std::string &name, const ValueRef &value, bool force)
{
  MetaClass *mc= this;
  MemberList::const_iterator mem, end;
  do
  {
    mem= mc->_members.find(name);
    end= mc->_members.end();

    mc= mc->_parent;
  } while (mc && (mem == end || mem->second.overrides == true || !mem->second.property->has_setter()));

  if (mem == end)
    throw bad_item(name);

  if (mem->second.read_only && !force)
  {
    if (mem->second.type.base.type == ListType || mem->second.type.base.type == DictType)
      throw grt::read_only_item(_name+"."+name+" (which is a container)");
    throw grt::read_only_item(_name+"."+name);
  }
  mem->second.property->set(object, value);
}


ValueRef MetaClass::get_member_value(const internal::Object *object, const std::string &name)
{
  MetaClass *mc= this;
  MemberList::const_iterator mem, end;
  do
  {
    mem= mc->_members.find(name);
    end= mc->_members.end();

    mc= mc->_parent;
  } while (mc && (mem == end || mem->second.overrides));

  if (mem == end || mem->second.property == NULL)
    throw bad_item(name);

  return mem->second.property->get(object);
}


ValueRef MetaClass::get_member_value(const internal::Object *object, const MetaClass::Member *member)
{
  return member->property->get(object);
}


ValueRef MetaClass::call_method(internal::Object *object, const std::string &name, const BaseListRef &args)
{
  MetaClass *mc= this;
  MethodList::const_iterator mem, end;
  do
  {
    mem= mc->_methods.find(name);
    end= mc->_methods.end();

    mc= mc->_parent;
  } while (mc && mem == end);

  if (mem == end)
    throw grt::bad_item(name);

  return (*mem->second.function)(object, args);
}



const MetaClass::Member* MetaClass::get_member_info(const std::string &member) const
{
  const MetaClass *mc= this;
  MemberList::const_iterator mem, end;
  do
  {
    mem= mc->_members.find(member);
    end= mc->_members.end();

    mc= mc->_parent;
  } while (mc && mem == end);

  if (mem == end)
    return 0;
  return &mem->second;
}


const MetaClass::Method* MetaClass::get_method_info(const std::string &method) const
{
  const MetaClass *mc= this;
  MethodList::const_iterator mem, end;
  do
  {
    mem= mc->_methods.find(method);
    end= mc->_methods.end();

    mc= mc->_parent;
  } while (mc && mem == end);

  if (mem == end)
    return 0;
  return &mem->second;
}


TypeSpec MetaClass::get_member_type(const std::string &member) const
{
  const Member *mem= get_member_info(member);
  if (!mem)
    throw bad_item(member);

  return mem->type;
}
