/* $Id$ 
 * 
 * Licensed to the Apache Software Foundation (ASF) under one or more 
 * contributor license agreements. See the NOTICE file distributed with  
 * this work for additional information regarding copyright ownership. 
 * The ASF licenses this file to you 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. 
 */ 

/*
 * etch_defvalufact.c 
 * default value factory from which all others inherit
 */

#include "etch_defvalufact.h"
#include "etch_serializer.h"
#include "etch_global.h"
#include "etchutl.h"
#include "etchmap.h"
#include "etchlog.h"
char* ETCHVALF = "VALF";

void etchvf_instantiate_builtins();

etch_type*    defvf_add_type (default_value_factory*, etch_type*);
etch_id_name* get_idname_by_id (etch_hashtable* map, const unsigned id); 
etch_type*    defvf_get_type_by_id (default_value_factory*,  const unsigned id);
etch_type*    defvf_get_type_by_name(default_value_factory*, const wchar_t* name); 
etch_type*    defvf_get_type_by_name(default_value_factory*, const wchar_t* name); 
wchar_t*      defvf_get_string_encoding (etch_value_factory*);
etch_type*    defvf_get_custom_struct_type (default_value_factory*, const unsigned);
etch_int64*   defvf_get_message_id  (default_value_factory*, etch_message*);
int           defvf_set_message_id  (default_value_factory*, etch_message*, etch_int64* id);
etch_int64*   defvf_get_in_reply_to (default_value_factory*, etch_message*);
int           defvf_set_in_reply_to (default_value_factory*, etch_message*, etch_int64* id);
etch_type*    defvf_get_mt_exception(default_value_factory*);
etch_type*    defvf_get_mt_rutime_exception(default_value_factory*);
etch_type*    defvf_get_mt_auth_exception(default_value_factory*);
objmask*      defvf_import_custom_value (default_value_factory*, etch_structvalue*);
etch_structvalue* defvf_export_custom_value (default_value_factory*, objmask* value);
etch_arraylist* new_vf_mixin_collection(default_value_factory*);
etch_arraylist* defvf_get_types (default_value_factory*);
int defvf_init_types(default_value_factory*, vf_idname_map*, class_to_type_map*);


/* - - - - - - - - - - - - - - - - - - - -
 * constructors/destructors
 * - - - - - - - - - - - - - - - - - - - -
 */

/**
 * new_default_value_factory()
 * constructor for default_value_factory
 * @param typemap the types map. if caller supplies this object, caller
 * retains ownership, unless vf.is_own_types flag is subsequently set false.
 * @param c2tmap the class to type map. if caller supplies this object, caller
 * retains ownership, unless vf.is_own_class_to_type flag is subsequently set false.
 */
default_value_factory* new_default_value_factory(vf_idname_map* typemap, 
    class_to_type_map* c2tmap)
{
    return new_default_value_factory_a(sizeof(default_value_factory), 
        typemap, c2tmap);
}


/**
 * new_default_value_factory_a()
 * constructor for default_value_factory
 * @param objsize size of the object footprint in bytes. 
 * @param typemap the types map. if caller supplies this object, caller
 * retains ownership, unless vf.is_own_types flag is subsequently set false.
 * @param c2tmap the class to type map. if caller supplies this object, caller
 * retains ownership, unless vf.is_own_class_to_type flag is subsequently set false.
 */
default_value_factory* new_default_value_factory_a(const int objsize, 
    vf_idname_map* typemap, class_to_type_map* c2tmap)
{
    i_value_factory* vtab;

    default_value_factory* newvf = (default_value_factory*) new_value_factory(objsize);

    newvf->destroy = destroy_default_value_factory;

    vtab = newvf->vtab;
    vtab->add_type              = defvf_add_type;
    vtab->export_custom_value   = defvf_export_custom_value;
    vtab->get_custom_struct_type= defvf_get_custom_struct_type;
    vtab->get_in_reply_to       = defvf_get_in_reply_to;
    vtab->get_message_id        = defvf_get_message_id;
    vtab->get_string_encoding   = defvf_get_string_encoding;
    vtab->get_type_by_id        = defvf_get_type_by_id;
    vtab->get_type_by_name      = defvf_get_type_by_name;
    vtab->get_types             = defvf_get_types;
    vtab->import_custom_value   = defvf_import_custom_value;
    vtab->set_in_reply_to       = defvf_set_in_reply_to;
    vtab->set_message_id        = defvf_set_message_id;

    defvf_init_types(newvf, typemap, c2tmap);

    newvf->mixins = new_vf_mixin_collection(newvf);

    return newvf;
}


/**
 * destroy_default_value_factory()
 * destructor for default_value_factory
 */
int destroy_default_value_factory(default_value_factory* vf)  
{   
    if (vf->refcount > 0 && --vf->refcount > 0) return -1;  

    if (!is_etchobj_static_content(vf))
    {   
        if (vf->mixins)
            vf->mixins->destroy(vf->mixins);

        /* class_to_type map must be destroyed prior to types map 
         * since class_to_type holds references to types map objects */
        if (vf->is_own_class_to_type) 
            vf->class_to_type->destroy(vf->class_to_type);

        if (vf->is_own_types)
            vf->types ->destroy(vf->types);  

        if (vf->impl) /* inheritor's instance object */
            vf->impl->destroy(vf->impl);
    }

   return destroy_objectex((objmask*) vf);
}


/**
 * defvf_init_types()
 * initialize a value factory object with fields and types.
 */
int defvf_init_types(default_value_factory* newvf,
    vf_idname_map* typemap, class_to_type_map* c2tmap)
{
    etch_hashtable* c2t = NULL;

    if (typemap)
        newvf->types = typemap;
    else
    {   newvf->types = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
        newvf->is_own_types = TRUE;
    }

    if (c2tmap)
        newvf->class_to_type = c2tmap;
    else 
    {   newvf->class_to_type = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
        newvf->is_own_class_to_type = TRUE;
    }

    etchvf_instantiate_builtins();   

    defvf_add_type(newvf, builtins._mt__etch_runtime_exception);
    defvf_add_type(newvf, builtins._mt__etch_auth_exception);
    defvf_add_type(newvf, builtins._mt__exception);
    defvf_add_type(newvf, builtins._mt__etch_list);
    defvf_add_type(newvf, builtins._mt__etch_map);
    defvf_add_type(newvf, builtins._mt__etch_set);
    defvf_add_type(newvf, builtins._mt__etch_datetime);

    c2t = newvf->class_to_type; 
    etch_serializer_exception_init(builtins._mt__exception, c2t);
    etch_serializer_authxcp_init  (builtins._mt__etch_auth_exception, c2t);
    etch_serializer_rtxcp_init    (builtins._mt__etch_runtime_exception, c2t);
    etch_serializer_list_init     (builtins._mt__etch_list,c2t);
    etch_serializer_map_init      (builtins._mt__etch_map, c2t);
    etch_serializer_set_init      (builtins._mt__etch_set, c2t);
    etch_serializer_date_init     (builtins._mt__etch_datetime, c2t);

    return 0;
}


/* - - - - - - - - - - - - - - - - - - - -
 * vf class methods
 * - - - - - - - - - - - - - - - - - - - -
 */

/**
 * defvf_add_type()
 * adds a type to set of types
 * @param type a non-disposable etch_type object
 * @return the effective type. if there was a name collision, the existing type
 * is returned in place of the supplied type, AND the supplied type is destroyed.
 * this simplifies logic up the line, and is consistent with caller expecting to
 * relinquish memory management of the type passed.
 */
etch_type* defvf_add_type (default_value_factory* vf, etch_type* type) 
{
    etch_type *effective_type = type;

    const int result = vf->types->vtab->inserth
        (vf->types->realtable, type, NULL, vf->types, 0); 

    if (-1 == result)
        effective_type = defvf_get_type_by_name(vf, type->name);

    if (effective_type != type && NULL != effective_type)
        destroy_static_type(type);

    #ifdef ETCH_DEBUG_VF
    if  (effective_type) 
         wprintf(L"add type %08x '%s'\n", 
            (size_t) (void*) effective_type, effective_type->name);
    else wprintf(L"error adding type '%s'\n", type->name);
    #endif

    return effective_type;   
}


/**
 * get_idname_by_id()
 * given a id_name map and a numeric id, return the id_name for that key. 
 * @return a non-disposable *reference*, not a copy, or null if not found.
 */
etch_id_name* get_idname_by_id (etch_hashtable* map, const unsigned id)
{
    etch_iterator iterator;
    set_iterator(&iterator, map, &map->iterable);

    while(iterator.has_next(&iterator))
    {
        etch_id_name* this_idname = (etch_id_name*) iterator.current_key;
        if (this_idname->id == id) return this_idname;
        iterator.next(&iterator);
    }

    return NULL;
}


/**
 * defvf_get_type_by_id()
 * @return a non-disposable reference to the type matching the supplied id,
 * or null if no match. all mixed-in vfs are recursed for a match.
 */
etch_type* defvf_get_type_by_id (default_value_factory* vf, const unsigned id)
{
    etch_type* type = get_idname_by_id(vf->types, id);
    if (type) return type;

    if (vf->mixins->count) 
    {
        etch_iterator iterator; 
        set_iterator(&iterator, vf->mixins, &vf->mixins->iterable);
 
        while(iterator.has_next(&iterator))
        {
            default_value_factory* mvf 
                = (default_value_factory*) iterator.current_value;

            if (is_etch_valuefact(mvf) && (mvf != vf))
            {
                type = defvf_get_type_by_id(mvf, id);
                if (type) return type; 
            }

            iterator.next(&iterator);
        }
    }

    return NULL;
}


/**
 * get_idname_by_name()
 * given a id_name map and a name string, return the id_name for that name. 
 * @return a non-disposable *reference*, not a copy, or null if not found.
 * recall that etch_type and etch_field each are typedefs of etch_id_name. 
 */
etch_id_name* get_idname_by_name(etch_hashtable* map, const wchar_t* name)
{
    etch_hashitem  hashbucket, *thisitem = &hashbucket; 
    const unsigned hash = etch_get_wchar_hashkey(name);
  
    const int result = map->vtab->findh(map->realtable, hash, map, &thisitem); 
 
    return result == 0? (etch_id_name*) thisitem->key: NULL;
}


/**
 * etchtypemap_get_by_name()
 * given a type map and a name, looks up the type matching that name, 
 * adding a new type to the map if no match was found.
 * @return a non-disposable reference to the type matching the supplied  
 * name, or null if no match.  
 */
etch_type* etchtypemap_get_by_name(etch_hashtable* map, const wchar_t* name)
{
    etch_type* thistype = get_idname_by_name(map, name);

    if (NULL == thistype) 
    {
        int result = map->vtab->inserth(map->realtable, 
            thistype = new_type(name), NULL, map, 0); 
    }
       
    return thistype;
}


/**
 * defvf_get_type_by_name()
 * searches vf and all mixed-in vfs for type matching specified name.
 * @return a non-disposable reference to the type corresponding to 
 * specified name, or null if no match.
 */
etch_type* defvf_get_type_by_name (default_value_factory* vf, const wchar_t* name) 
{
    etch_type* type = get_idname_by_name(vf->types, name);
    if (type) return type;

    if (vf->mixins->count) 
    {
        etch_iterator iterator; 
        set_iterator(&iterator, vf->mixins, &vf->mixins->iterable);
 
        while(iterator.has_next(&iterator))
        {
            default_value_factory* mvf 
                = (default_value_factory*) iterator.current_value;

            if (is_etch_valuefact(mvf) && (mvf != vf)) 
            {
                type = defvf_get_type_by_name(mvf, name);
                if (type) return type; 
            }

            iterator.next(&iterator);
        }
    }

    return NULL;
}

	
/**
 * defvf_get_types()
 * @return a disposable arraylist of non-disposable references to all types 
 * resident in this vf, and in all its mixed-in vfs. caller should invoke 
 * the returned list's destructor, which will destroy the list shell.
 */
etch_arraylist* defvf_get_types (default_value_factory* vf)
{    
    etch_arraylist* typeslist = get_map_keys(vf->types);

    if (vf->mixins->count) 
    {
        int  newmixintypes = 0;
        etch_iterator iterator; 
        set_iterator(&iterator, vf->mixins, &vf->mixins->iterable);
 
        while(iterator.has_next(&iterator))
        {
            etch_arraylist* mvflist;
 
            default_value_factory* mvf 
                = (default_value_factory*) iterator.current_value;

            if (is_etch_valuefact(mvf) && (mvf != vf)) 
            {
                mvflist = get_map_keys(mvf->types);

                newmixintypes += arraylist_add_from
                    (typeslist, mvflist, etchtypelist_comparator); 
                    
                mvflist->destroy(mvflist);
            }

            iterator.next(&iterator);
        }
    }

    return typeslist; /* caller must dispose */
}


/**
 * defvf_get_string_encoding()
 * @returns a non-disposable reference to the encoding type
 */
wchar_t* defvf_get_string_encoding (etch_value_factory* vf)
{
    return (wchar_t*) str_utf8;
}


/**
 * get_etch_string_encoding()
 * return etch code indicating current string encoding
 */
int get_etch_string_encoding(etch_value_factory* vf)
{
    wchar_t* encoding = vf->vtab->get_string_encoding(vf);
    if (0 == wcscmp(encoding, L"utf-8"))  return ETCH_ENCODING_UTF8; 
    if (0 == wcscmp(encoding, L"utf-16")) return ETCH_ENCODING_UTF16;
    return ETCH_ENCODING_ASCII;     
} 


/**
 * defvf_get_custom_struct_type()
 * @return a non-disposable reference to the etch type type corresponding   
 * to the specified class. invoked recursively via mixed in vfs. 
 */
etch_type* defvf_get_custom_struct_type(default_value_factory* vf, 
    const unsigned etchclass)
{
    etch_type* type = (etch_type*) etchmap_find(vf->class_to_type, etchclass, 0);
    if (type) return type;

    if (vf->mixins->count) 
    {
        etch_iterator iterator; 
        set_iterator(&iterator, vf->mixins, &vf->mixins->iterable);
 
        while(iterator.has_next(&iterator))
        {
            default_value_factory* mvf 
                = (default_value_factory*) iterator.current_value;
                  
            if (is_etch_valuefact(mvf) && (mvf != vf)) /* ensure no cycle */
            {
                type = mvf->vtab->get_custom_struct_type(mvf, etchclass);
                if (type) break;
            }

            iterator.next(&iterator);
        }
    }

    return type;
}
	

/**
 * defvf_import_custom_value()
 * get helper method from the struct's type to import the custom value associated
 * with the type. if found, invoke the method to create the custom object.
 * @param sv the *disposable* raw key-value pairs read in by tdi,
 * from which to reconstruct the expected custom value object.
 * caller relinquishes ownership of this object regardless of outcome.
 * @return the *disposable* custom object, which must be cast by caller, or null.
 */
objmask* defvf_import_custom_value (default_value_factory* vf, etch_structvalue* sv)
{  
    objmask* custom_value = NULL;

    etch_serializer* impxhelper = etchtype_get_impexphelper(sv->struct_type);
   
    if (impxhelper)
        custom_value = impxhelper->import_value(impxhelper, (objmask*) sv);
     
    sv->destroy(sv);

    return custom_value;
}


/**
 * defvf_export_custom_value()
 * establishes the etch type of the custom struct to be exported to, gets the
 * export helper method from the type, and invokes that method to do the export.
 * @param value the custom value object to be exported to a struct for tdo.
 * caller retains ownership of this object.
 * @return the exported struct, or null. caller owns the returned struct.
 */
etch_structvalue* defvf_export_custom_value(default_value_factory* vf, objmask* value)
{
    etch_type* custom_type = NULL;
    etch_serializer*  impxhelper = NULL; 
    etch_structvalue* exported_value = NULL;
    if (NULL == value) return NULL;

    custom_type = defvf_get_custom_struct_type(vf, ETCHOBJCLASS(value));

    if (NULL == custom_type)
    {
        switch(value->obj_type)
        { 
          case ETCHTYPEB_STRUCTVAL:
               custom_type = ((etch_structvalue*)value)->struct_type;
               break;
          case ETCHTYPEB_EXCEPTION:
               custom_type = builtins._mt__etch_runtime_exception;
               break;
          case ETCHTYPEB_ETCHLIST:
               custom_type = builtins._mt__etch_list;
               break;
          case ETCHTYPEB_ETCHMAP:
               custom_type = builtins._mt__etch_map;
               break;
          case ETCHTYPEB_ETCHSET:
               custom_type = builtins._mt__etch_set;
               break;
        }
    }

    /* fetch non-disposable helper object from custom type */
    impxhelper = etchtype_get_impexphelper(custom_type);

    if (impxhelper && value)
    {   /* export value object to struct */
        exported_value = (etch_structvalue*) 
            impxhelper->export_value(impxhelper, value);

        /* changed to not destroy value 6/23, since the tdo itself does not own 
         * the value, and the export custom value is invoked from tdo write_value. */
        /* value->destroy(value); */
    }

    return exported_value;
}


/**
 * defvf_add_mixin()
 * add a mixed in value factory to this value factory.
 * @return 0 or -1.
 */
int defvf_add_mixin(default_value_factory* vf, etch_value_factory* mixedin_vf)
{
    return arraylist_add(vf->mixins, mixedin_vf);
}


/**
 * defvf_get_message_id()
 * valuefactory.get_message_id() implementation.
 * @return a non-disposable reference to the etch_int64 id object, or null. 
 */
etch_int64* defvf_get_message_id (default_value_factory* vf, etch_message* msg)
{
    return (etch_int64*) message_get(msg, builtins._mf__message_id);
}


/**
 * message_get_id32()
 * get and return message id in 32 bits. used for debugging, logging, etc.
 */
unsigned message_get_id32 (etch_message* msg)
{
    default_value_factory* vf = (default_value_factory*) msg->vf;
    etch_int64* id64 = (etch_int64*) message_get (msg, builtins._mf__message_id);
    const int id32 = id64? (unsigned) id64->value: 0;
    return id32;
}


/**
 * defvf_set_message_id()
 * valuefactory.set_message_id() implementation.
 * @param id a *disposable* etch_int64* wrapping the message id. 
 * regardless of outcome, caller relinquishes ownership of this object.
 * @return 0 or -1.
 */
int defvf_set_message_id (default_value_factory* vf, etch_message* msg, etch_int64* id)
{
    return message_put(msg, clone_field(builtins._mf__message_id), (objmask*) id); 
}


/**
 * defvf_get_in_reply_to() 
 * gets the id of the message to which this message is a reply.
 * returns a non-disposable reference to an etch_int64*, not a copy, or null.
 */
etch_int64* defvf_get_in_reply_to (default_value_factory* vf, etch_message* msg)
{
    return (etch_int64*) message_get(msg, builtins._mf__in_reply_to);
}


/**
 * defvf_set_in_reply_to() 
 * sets the id of the message to which this message is a reply.
 * @param id a *disposable* etch_int64* wrapping the message id.  
 * regardless of outcome, caller relinquishes ownership of this object.
 * @return 0 or -1.
 */
int defvf_set_in_reply_to (default_value_factory* vf, etch_message* msg, etch_int64* id)
{
    return message_put (msg, clone_field(builtins._mf__in_reply_to), (objmask*) id); 
}


/* - - - - - - - - - - - - - - - - - - - -
 * vf types collection
 * - - - - - - - - - - - - - - - - - - - -
 */

/* 
 * defvf_typescollection_clear_handler()
 * callback set to handle freeing of key memory during a clear() of the vf
 * types collection. such a map owns its content, which consists of both builtin
 * types, which are marked static, and of user types, which are generally not
 * marked static. the type's destructor is invoked here; however the destructor
 * will take no action on the static types (these being freed later when the 
 * builtins are destroyed. those types which are added as a result of a runtime
 * get() call, e.g., etchtypemap_get_by_name(vf->types, L"my_newtype"),
 * are not marked static and so will be destroyed with this map.
 */
int defvf_typescollection_clear_handler (objmask* key, objmask* value)  
{
    #if(0)
    etch_type* type = (etch_type*) key;
    if  (is_etchobj_static_shell(type))     
         wprintf(L"vf types map NOT destroying %s\n", type->name);
    else wprintf(L"vf types map destroying %s\n", type->name);
    fflush(stdout);  
    #endif

    key->destroy(key); /* see comments above re static and nonstatic types */
    return TRUE; 
}
 

/**
 * new_vf_types_collection()
 * return a hashtable configured as expected for a set of types 
 */
etch_set* new_vf_types_collection(const int initialsize)
{
    etch_set* map = new_set(initialsize);  
    map->content_obj_type = ETCHTYPEB_TYPE;
    map->content_class_id = CLASSID_ID_TYPE;  
    map->freehook = defvf_typescollection_clear_handler;  
    return map;
}


/* - - - - - - - - - - - - - - - - - - - -
 * vf_idname_map
 * - - - - - - - - - - - - - - - - - - - -
 */

/* 
 * defvf_idnmap_clear_handler()
 * callback set to handle freeing of key memory during a clear() of a vf
 * id_name map. map keys are etch_field owned by the map. however the map does
 * not know how to destroy such a key so we handle that here. values in this
 * map are etch_type owned elsewhere (presumably quasi-static built-in types).
 */
int defvf_idnmap_clear_handler (objmask* key, objmask* value)  
{
    #ifdef ETCH_DEBUG_VF
    etch_id_name* idn = (etch_id_name*) key;
    wprintf(L"destroy idn %08x '%s'\n", (size_t) (void*) key, idn->name);
    #endif
 
    key->destroy(key);
    return TRUE; 
}
 

/**
 * new_vf_idname_map()
 * return a hashtable configured as expected for a vf 
 * todo lose this method and the clear handler we don't use it 
 */
etch_hashtable* new_vf_idname_map(const int initialsize)
{
    etch_hashtable* map = new_hashtable(initialsize);  
    map->is_tracked_memory  = TRUE; 
    map->is_readonly_keys   = FALSE; /* keys are etch_field* owned by the map */ 
    map->is_readonly_values = FALSE; /* values are etch_type* owned elsewhere */ 
    map->content_type       = ETCHHASHTABLE_CONTENT_OBJECT;
    map->content_obj_type   = ETCHTYPEB_TYPE;
    map->content_class_id   = CLASSID_ID_TYPE;  
    map->freehook = defvf_idnmap_clear_handler; /* key memory free hook */
    return map;
}


/* - - - - - - - - - - - - - - - - - - - -
 * class_to_type_map
 * - - - - - - - - - - - - - - - - - - - -
 */

/**
 * class_to_type_map_put()
 * put the specified type to the supplied map, keyed by specified class.
 * the map does not own the specified type object.
 * @return 0 or -1;
 */
int class_to_type_map_put(class_to_type_map* map, const unsigned thisclass, 
    etch_type* type)
{
    int hashvalue = etchmap_insert (map, thisclass, type, TRUE);
    return hashvalue? 0: -1;
}


/**
 * class_to_type_map_get()
 * get the type associated with the specified class from the supplied map.
 * @return a non-disposable reference to the matching type, or null.
 */
etch_type* class_to_type_map_get(class_to_type_map* map, const unsigned thisclass) 
{
    etch_type* foundobj = (etch_type*) etchmap_find (map, thisclass, NULL);
    return foundobj;
}


/**
 * new_class_to_type_map()
 * class_to_type_map constructor.
 * such a map associates a "class", which in this context is a 32-bit unsigned
 * integer whose high 16 bits is the etch object obj_type, and the low 16 bits
 * the etch object class_id, to an etch_type. 
 */
class_to_type_map* new_class_to_type_map(const int initialsize)
{
    /* this map calls no etch destructors when destroyed. the key strings are 
     * freed automatically by the etch_hashtable, and the values are pointers
     * to etch_type objects owned elsewhere. it obviously follows that this 
     * map should be destroyed prior to any such referenced type object. 
     */
    class_to_type_map* map  = new_hashtable(initialsize);  
    map->is_tracked_memory  = TRUE; 
    map->is_readonly_keys   = TRUE; /* keys are strings owned by the hashtable */ 
    map->is_readonly_values = TRUE; /* values are non-disposable etch_type* */ 
    map->content_type       = ETCHHASHTABLE_CONTENT_OBJECT;
    map->content_obj_type   = ETCHTYPEB_TYPE;
    map->content_class_id   = CLASSID_ID_TYPE;  
    return map;
}


/* - - - - - - - - - - - - - - - - - - - - - - -
 * built-in (etch-global, quasi-static) objects
 * - - - - - - - - - - - - - - - - - - - - - - -
 */

/* these objects are global to all vfs.
 * for each such object, a corresponding entry should exist both in 
 * etchvf_instantiate_builtins(), and in etchvf_free_builtins()
 */
const wchar_t* str_etch_runtime_exception = L"_Etch_RuntimeException";   
const wchar_t* str_etch_auth_exception    = L"_Etch_AuthException";         
const wchar_t* str_exception     = L"_exception"; 
const wchar_t* str_etch_list     = L"_Etch_List"; 
const wchar_t* str_etch_map      = L"_Etch_Map";
const wchar_t* str_etch_set      = L"_Etch_Set";
const wchar_t* str_etch_datetime = L"_Etch_Datetime";
 
const wchar_t* str_msg           = L"msg";   
const wchar_t* str_message_id    = L"_messageId";  
const wchar_t* str_in_reply_to   = L"_inReplyTo";  
const wchar_t* str_result        = L"result";
   
const wchar_t* str_utf8          = L"utf-8"; 
const wchar_t* str_keys          = L"keys";    
const wchar_t* str_values        = L"values";  
const wchar_t* str_date_time     = L"dateTime";
const wchar_t* str_keys_values   = L"keys_values"; 

const wchar_t* str_msgizervf     = L"Messagizer.valueFactory";
const wchar_t* str_msgizerfmt    = L"Messagizer.format";


/* 
 * etchvf_free_builtins()
 * frees memory for etch-global quasi-static builtin objects,
 * and for the validators cache and its validator content.
 * it should be invoked at etch teardown, after last vf is destroyed.
 * unit tests will show memory leaks unless they invoke this post-test.
 */

void etchvf_free_builtins()
{
    if (is_builtins_instantiated)  
    {
        destroy_static_type(builtins._mt__etch_runtime_exception);
        destroy_static_type(builtins._mt__etch_auth_exception);
        destroy_static_type(builtins._mt__exception);
        destroy_static_type(builtins._mt__etch_list); 
        destroy_static_type(builtins._mt__etch_map); 
        destroy_static_type(builtins._mt__etch_set); 
        destroy_static_type(builtins._mt__etch_datetime); 

        destroy_static_field(builtins._mf_msg);
        destroy_static_field(builtins._mf_result);
        destroy_static_field(builtins._mf__message_id);
        destroy_static_field(builtins._mf__in_reply_to);
    }

    etchvtor_clear_cache();  /* destroy cached validators */

    is_builtins_instantiated = FALSE;
}


/**
 * etchvf_instantiate_builtins()
 * instantiate built-in objects such as default types and fields.
 * these singleton objects are destroyed explicitly by invoking  
 * etchvf_free_builtins().
 * todo: mark these objects immutable and unmark in destructor.
 * todo: determine how we handle case of multiple vfs.
 */
void etchvf_instantiate_builtins()
{  
    if (is_builtins_instantiated) return;

    builtins._mt__etch_runtime_exception = new_static_type(str_etch_runtime_exception);
    builtins._mt__etch_auth_exception = new_static_type(str_etch_auth_exception);
    builtins._mt__exception = new_static_type(str_exception);
    builtins._mt__etch_list = new_static_type(str_etch_list); 
    builtins._mt__etch_map  = new_static_type(str_etch_map); 
    builtins._mt__etch_set  = new_static_type(str_etch_set); 
    builtins._mt__etch_datetime = new_static_type(str_etch_datetime); 

    builtins._mf_msg = new_static_field(str_msg); 
    builtins._mf_result = new_static_field(str_result); 
    builtins._mf__message_id  = new_static_field(str_message_id); 
    builtins._mf__in_reply_to = new_static_field(str_in_reply_to); 

    etchtype_put_validator(builtins._mt__exception, 
        clone_field(builtins._mf_result), (objmask*) etchvtor_exception_get());
    etchtype_put_validator(builtins._mt__exception, 
        clone_field(builtins._mf__message_id), (objmask*) etchvtor_int64_get(0));
    etchtype_put_validator(builtins._mt__exception, 
        clone_field(builtins._mf__in_reply_to),(objmask*) etchvtor_int64_get(0));

    is_builtins_instantiated = TRUE;
}


/* - - - - - - - - - - - - - - - - - - - -
 * other methods
 * - - - - - - - - - - - - - - - - - - - -
 */
	
/**
 * etchtypelist_comparator()
 * etch_comparator comparing two etch_types
 * hook for arraylist_add_from to determine if a type from list b
 * already exists in list a.
 */
int etchtypelist_comparator(void* a, void* b)
{
    return a && b && (((etch_type*)a)->id == ((etch_type*)b)->id);
}


/**
 * new_vf_mixin_collection()
 * instantiate and return a collection configured appropriately for the
 * storage of value factory objects of mixed-in classes.
 */
etch_arraylist* new_vf_mixin_collection(default_value_factory* vf)
{
    etch_arraylist* list = new_arraylist(ETCH_DEVVF_MIXINS_DEFINITSIZE, 0);
    list->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
    list->is_readonly  = TRUE; /* list dtor will not destroy list content */
    list->content_obj_type = vf->obj_type;
    list->content_class_id = vf->class_id;
    return list;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * serializable exception tossing  
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */	

  
/**
 * message_throw()
 * instantiate a serializable exception object from the exception indicated by
 * the specified parameters, and add that exception to the specified message.
 * @param msg the message.
 * @param xtype type of exception, excptype enumeration.
 * @param xtext the exception text, caller retains.
 * @return 0 success, -1 failure.
 */
int message_throw (etch_message* msg, excptype xtype, wchar_t* xtext)
{
    int result = 0;
    etch_exception* excpobj = NULL;
    if (NULL == msg) return -1;

    excpobj = new_etch_exception (xtype, xtext, ETCHEXCP_COPYTEXT);
    result  = message_put (msg, clone_field(builtins._mf_result), (objmask*) excpobj);
    return result;
} 


/**
 * message_throw_from()
 * instantiate a serializable exception object from the exception in the 
 * specified etch object, and add that exception to the specified message.
 * @param msg the message.
 * @param obj the object wrapping the exception, or which is the exception,
 * caller retains.
 * @return 0 success, -1 failure.
 */
int message_throw_from (etch_message* msg, objmask* obj)
{
    etchexception* x = get_exception_from (obj);
    if (NULL == x) return NULL;

    return message_throw (msg, x->excptype, x->excptext);
}







