/* $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_serializer.c
 */

#include "etch_serializer.h"
#include "etch_defvalufact.h"
#include "etch_arrayval.h"
#include "etch_encoding.h"
#include "etch_syncobj.h"
#include "etch_global.h"
#include "etchexcp.h"
#include "etchmap.h"
#include "etchlog.h"
char* ETCHIMPX = "IMPX";

objmask* etchserializer_defexportval (etch_serializer* thisx, objmask* objval);
objmask* etchserializer_defimportval (etch_serializer* thisx, objmask* structval);
int destroy_etch_serializer(etch_serializer* thisx);


/* - - - - - - - - - - - - - - - - - -
 * constructors/destructors
 * - - - - - - - - - - - - - - - - - -  
 */

/**
 * new_etch_serializer() 
 * etch_serializer constructor
 */
etch_serializer* new_etch_serializer(const int objsize) 
{
    const int size = objsize? objsize: sizeof(etch_serializer);

    etch_serializer* newobj = (etch_serializer*) new_object
        (sizeof(etch_serializer), ETCHTYPEB_SERIALIZER, CLASSID_NONE);

    newobj->destroy = destroy_etch_serializer;
    newobj->import_value = etchserializer_defimportval;
    newobj->export_value = etchserializer_defexportval;

    return newobj;
}


/** 
 * destroy_etch_serializer()
 * etch_serializer destructor
 */
int destroy_etch_serializer(etch_serializer* thisx)
{
    if (thisx->refcount > 0 && --thisx->refcount > 0) return -1;   

    if (!is_etchobj_static_content(thisx))  
    {
        if (thisx->impl)
        {   thisx->impl->destroy(thisx->impl);
            thisx->impl = NULL;
        }
    }

    return destroy_objectex((objmask*) thisx);
} 


/**
 * etch_serializer_init() 
 * generic static intitializer for import export helper serializers.
 * caller retains ownership of *all* parameters to this method.
 * @param obj_type type of the object being serialized
 * @param keyname name of an existing etch_field which will key the import
 * and export object in its serialization map. 
 * @param obj_class numeric etch object type and class id of the object 
   being serialized.  
 * @param c2tmap a vf's map of numeric class to object type associations.
 * @param vtor the validator object used to vet objects of this type.
 * @param new_szr a pointer to the constructor for serializers of the 
 * specified type.
 */
int etch_serializer_init(etch_type* obj_type, const wchar_t* keyname, 
    const unsigned obj_class, etch_hashtable* c2tmap, etch_validator* vtor, 
    etch_serializer_ctor new_szr)
{
    etch_serializer* newimpexhelper = NULL;
    /* serializer instantiation is awkward, since we are installing serializers 
     * to singleton types, but the class to type map is not singleton. 
     * investigate, and if reasonable, move the c2t maintenance to a higher  
     * level, and invoke this code only once per type instantiation.
     * unfortunately a type does not indicate its correpsonding class,
     * i.e. we know a type is "foo", but the type does not know it maps to
     * ETCHTYPE_FOO, CLASSID_FOO. so here, the serializers, in their ctors,
     * determine that mapping, and pass that class here. ideally we can eliminate
     * both the c2t and class parameters from the serializer initialization.
     */

    /* get reference to field key having specified name, not owned here */
    etch_field* szr_key = etchtype_get_field_by_name (obj_type, keyname);
    if (NULL == szr_key) return -1; 
 
    /* map this class identifier to associated type object */
    class_to_type_map_put(c2tmap, obj_class, obj_type);

    /* if the type's serializer is already instantiated, we don't want   
     * to do so again. this is the awkardness commented above. */
    if (etchtype_get_component_type(obj_type)) return 0; 

    /* set component type for arrays of specified class */
    etchtype_set_component_type(obj_type, obj_class);

    /* instantiate the new import/export helper */ 
    newimpexhelper = new_szr(obj_type, szr_key); 
   
    /* attach, and assign ownership of, the new helper object to the type */
    etchtype_set_impexphelper(obj_type, newimpexhelper);
    
    return NULL == vtor? 0: /* attach the supplied validator to the type */
           etchtype_put_validator(obj_type, clone_field(szr_key), (objmask*) vtor);
}


/* - - - - - - - - - - - - - - - - - -
 * exception serializer
 * - - - - - - - - - - - - - - - - - -  
 */

/**
 * etchserializer_exception_export_value() 
 * export value for any exception
 * @param objval an etch_exception object. caller owns it,
 * and presumably will destroy it upon return from this method.
 * @return the exported disposable structvalue object. caller must cast it.
 */
objmask* etchserializer_exception_export_value (etch_serializer* thisx, objmask* objval)
{
    int result = 0;
    char* excpvalue = NULL;
    const int THISINITSIZE = 2;
    etch_string* stringobj = NULL;
    etch_structvalue* structval = NULL;

    etch_exception* excpobj = (etch_exception*) objval;
    if (!is_etch_exception(excpobj)) 
    {   etchlog(ETCHIMPX, ETCHLOG_ERROR, "value is not exception\n");
        return NULL;
    }

    /* we tostring() the exception, getting back a buffer which we own here.
     * we pass ownership of that buffer to an etch_string, and then pass 
     * ownership of that etch_string to the returned structvalue. recall that
     * a structvalue owns all its content memory, and assumes its keys and
     * values are etch objects on which it will invoke destructors when it
     * itself is destroyed. we clone a type key for a similar reason.
     */
    excpvalue = excp_tostringx(excpobj->value);
    stringobj = new_string_from(excpvalue, ETCH_ENCODING_ASCII);
    structval = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
    result = structvalue_put(structval, clone_field(thisx->field), (objmask*)stringobj);

    return (objmask*) structval;
}


/**
 * etchserializer_exception_import_value() 
 * import value for any exception.
 * @param objval an etch_structvalue of appropriate type for the exception.
 * caller retains ownership of this object as with all imports.
 * @return an opaque etch object containing the imported exception. 
 *
 * caller owns and must destroy the returned object.  
 * use: etch_exception* x = get_exception(returnedobj);
 * to point into and inspect the exception.
 */
objmask* etchserializer_exception_import_value
   (etch_serializer* thisx, objmask* objval, excptype xtype) 
{
    wchar_t* unistring = NULL;
    etch_string* impstring = NULL;
    etch_exception* exception = NULL;

    etch_structvalue* structval = (etch_structvalue*) objval;
    if (!is_etch_struct(structval)) return NULL;
    if (!structvalue_is_type(structval, thisx->type)) return NULL;

    /* fetch the exception string out of the struct. not owned here. */
    impstring = (etch_string*) structvalue_get (structval, thisx->field);
    if (!is_etch_string(impstring)) return NULL;

    if (IS_ETCH_ENCODING_8BIT(impstring->encoding))
        /* recall that we carry all non-internal strings as unicode.
         * we own the returned unicode buffer for the moment. */
        etch_ansi_to_unicode (&unistring, impstring->v.valc);
    else /* todo figure a way to not re-allocate the string */
        unistring = new_wchar (impstring->v.valw);

    /* create an exception object from the deserialized string. this object 
     * manages the exception memory according to flags supplied it. we set 
     * such a flag here, ETCHEXCP_FREETEXT, indicating that the exception 
     * owns the text buffer we give it. thus we no longer own unistring */
    exception = new_etch_exception (xtype, unistring, ETCHEXCP_FREETEXT);

    return (objmask*) exception;
}


/**
 * etchserializer_excp_import_value() 
 * virtual import value for runtime exception.
 * @param objval an etch_structvalue of type etch runtime exception.
 * caller retains ownership of this object as with all imports.
 * @return an opaque etch object containing the imported exception. 
 *
 * caller owns and must destroy the returned object.  
 * use: etch_exception* x = get_exception(returnedobj);
 * to point into and inspect the exception.
 */
objmask* etchserializer_excp_import_value(etch_serializer* thisx, objmask* objval) 
{
    return etchserializer_exception_import_value(thisx, objval, EXCPTYPE_GENERIC);
}



/**
 * new_exception_serializer() 
 * etch_serializer_excp constructor
 * @param type  - not owned
 * @param field - not owned
 */
etch_serializer* new_exception_serializer(etch_type* type, etch_field* field) 
{
    etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);

    newobj->class_id = CLASSID_SERIALIZER_EXCP;
    newobj->type  = type;  /* not owned */
    newobj->field = field; /* not owned */

    newobj->export_value = etchserializer_exception_export_value;
    newobj->import_value = etchserializer_excp_import_value;

    return newobj;
}


/**
 * etch_serializer_exception_init() 
 * static intitializer for runtime exception serializer.
 * creates the impexp serializer and installs it to the supplied type.
 * @param thistype type of the serializer (runtime exception)
 */
int etch_serializer_exception_init(etch_type* thistype, etch_hashtable* c2tmap)
{
    const wchar_t* keyname = str_msg; /* vf static constant */
    const unsigned thisclass 
        = ETCHMAKECLASS(ETCHTYPEB_EXCEPTION, CLASSID_EXCEPTION);
    
    return etch_serializer_init(thistype, keyname, thisclass, c2tmap, 
        etchvtor_string_get(0), new_exception_serializer);
}


/* - - - - - - - - - - - - - - - - - -
 * runtime exception serializer
 * - - - - - - - - - - - - - - - - - -  
 */

/**
 * etchserializer_rtxcp_export_value() 
 * virtual export value for runtime exception
 * @param objval a, etchexception blob, caller owns it and presumably will
 * destroy it upon return from this method.
 * @return the exported disposable structvalue object. caller must cast it.
 */
objmask* etchserializer_rtxcp_export_value(etch_serializer* thisx, objmask* objval) 
{
    return etchserializer_exception_export_value(thisx, objval);
}


/**
 * etchserializer_rtxcp_import_value() 
 * virtual import value for runtime exception.
 * @param objval an etch_structvalue of type etch runtime exception.
 * caller retains ownership of this object as with all imports.
 * @return an opaque etch object containing the imported exception. 
 *
 * caller owns and must destroy the returned object.  
 * use: etch_exception* x = get_exception(returnedobj);
 * to point into and inspect the exception.
 */
objmask* etchserializer_rtxcp_import_value(etch_serializer* thisx, objmask* objval) 
{
    return etchserializer_exception_import_value(thisx, objval, EXCPTYPE_ETCHRUNTIME);
}


/**
 * new_runtime_exception_serializer() 
 * etch_serializer_rtxcp constructor
 * @param type  - not owned
 * @param field - not owned
 */
etch_serializer* new_runtime_exception_serializer(etch_type* type, etch_field* field) 
{
    etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);

    newobj->class_id = CLASSID_SERIALIZER_RTXCP;
    newobj->type  = type;  /* not owned */
    newobj->field = field; /* not owned */

    newobj->export_value = etchserializer_rtxcp_export_value;
    newobj->import_value = etchserializer_rtxcp_import_value;

    return newobj;
}


/**
 * etch_serializer_rtxcp_init() 
 * static intitializer for runtime exception serializer.
 * creates the impexp serializer and installs it to the supplied type.
 * @param thistype type of the serializer (runtime exception)
 */
int etch_serializer_rtxcp_init(etch_type* thistype, etch_hashtable* c2tmap)
{
    const wchar_t* keyname = str_msg; /* vf static constant */
    const unsigned thisclass 
        = ETCHMAKECLASS(ETCHTYPEB_EXCEPTION, CLASSID_RUNTIME_EXCEPTION);
    
    return etch_serializer_init(thistype, keyname, thisclass, c2tmap, 
        etchvtor_string_get(0), new_runtime_exception_serializer);
}


/* - - - - - - - - - - - - - - - - - -
 * auth exception serializer
 * - - - - - - - - - - - - - - - - - -  
 */

/**
 * etchserializer_authxcp_export_value() 
 * virtual export value for auth exception
 * @param objval a, etchexception blob, caller owns it and presumably will
 * destroy it upon return from this method.
 * @return the exported disposable structvalue object. caller must cast it.
 */
objmask* etchserializer_authxcp_export_value(etch_serializer* thisx, objmask* objval) 
{
    return etchserializer_exception_export_value(thisx, objval);
}


/**
 * etchserializer_authxcp_import_value() 
 * virtual import value for runtime exception.
 * @param objval an etch_structvalue of type etch runtime exception.
 * caller retains ownership of this object as with all imports.
 * @return a nonspecific etch object containing the imported exception. 
 *
 * caller owns and must destroy the returned object.  
 * use: etch_exception* x = get_exception(returnedobj);
 * to point into and inspect the exception.
 */
objmask* etchserializer_authxcp_import_value(etch_serializer* thisx, objmask* objval) 
{
    return etchserializer_exception_import_value(thisx, objval, EXCPTYPE_ETCHAUTH);
}


/**
 * new_auth_exception_serializer() 
 * etch_serializer_auth exception constructor
 * @param type  - not owned
 * @param field - not owned
 */
etch_serializer* new_auth_exception_serializer(etch_type* type, etch_field* field) 
{
    etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);

    newobj->class_id = CLASSID_SERIALIZER_AUTHXCP;
    newobj->type  = type;  /* not owned */
    newobj->field = field; /* not owned */

    newobj->export_value = etchserializer_authxcp_export_value;
    newobj->import_value = etchserializer_authxcp_import_value;

    return newobj;
}


/**
 * etch_serializer_authxcp_init() 
 * static intitializer for auth exception serializer.
 * creates the impexp serializer and installs it to the supplied type.
 * @param thistype type of the serializer (runtime exception)
 */
int etch_serializer_authxcp_init(etch_type* thistype, etch_hashtable* c2tmap)
{
    const wchar_t* keyname = str_msg; /* vf static constant */
    const unsigned thisclass 
        = ETCHMAKECLASS(ETCHTYPEB_EXCEPTION, CLASSID_AUTH_EXCEPTION);

    return etch_serializer_init(thistype, keyname, thisclass, c2tmap, 
        etchvtor_string_get(0), new_auth_exception_serializer);
}


/* - - - - - - - - - - - - - - - - - -
 * list serializer
 * - - - - - - - - - - - - - - - - - -  
 */

/**
 * etchserializer_list_export_value() 
 * virtual export value for list
 * @param objval an etch_arraylist containing values to export to tdo. 
 * caller retains ownership of this object; this method however sets the readonly
 * mark on this object, such that it will not destroy its content, which was
 * assigned to the arraylist wrapped in the returned struct. 
 * the c binding internally models a list type as an etch_arraylist.
 * @return the exported structvalue object, containing an etch_arraylist. 
 * since we can't pass around raw arrays as does the java binding in this case,
 * this serializer chooses to use an arraylist as the export object type for lists.
 * while it may seem convoluted that the exported from and exported to objects are
 * the same object type (not the same object however), the reason is the vf export
 * interface contract, i.e. caller relinquishes memory for the export from object,
 * and acquires memory for the export to objects.
 */
objmask* etchserializer_list_export_value(etch_serializer* thisx, objmask* objval) 
{
    int result = 0;
    const int THISINITSIZE = 2;
    etch_arraylist* listobj = NULL;
    etch_arraylist* listnew = NULL;    
    etch_structvalue* outstruct = NULL;
    if (!is_etch_arraylist(objval)) return NULL;
    
    /* copy caller's list's content to a new list */
    listobj = (etch_arraylist*) objval;
    listnew = new_synchronized_arraylist_from(listobj);   
    /* value factory export_custom_value() will destroy() the export from object;
     * we mark that object such that its content will not be destroyed with it */
    listobj->is_readonly = TRUE;   
   
    /* assign ownership of the new list to the returned struct */
    outstruct = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
    result = structvalue_put(outstruct, clone_field(thisx->field), (objmask*) listnew);

    return (objmask*) outstruct; /* caller owns this structvalue */
}


/**
 * etchserializer_list_import_value() 
 * virtual import value for etch list.
 * @param objval an etch_structvalue of type etch list.
 * caller retains ownership of this object as with all imports.
 * @return a *disposable* etch_arraylist containing the imported data objects. 
 *
 * note that the c binding works a bit differently that does the java here.
 * java tdi populates the structvalue with an object[]. in c the tdi inserts a 
 * synchronized etch_arraylist to the structvalue. 
 */
objmask* etchserializer_list_import_value(etch_serializer* thisx, objmask* objval) 
{
    etch_arraylist *inarray = NULL, *outarray = NULL;
    etch_structvalue* instruct = (etch_structvalue*) objval;
    if (!is_etch_struct(instruct)) return NULL;
    if (!structvalue_is_type(instruct, thisx->type)) return NULL;

    /* fetch the arrayvalue from the struct. struct owns it. */
    inarray = (etch_arraylist*) structvalue_get(instruct, thisx->field);
    if (!is_etch_arraylist(inarray)) return NULL;

    /* copy content to return list */
    outarray = new_synchronized_arraylist_from(inarray);  
 
    /* value factory import_custom_value() will destroy() the import struct
     * which will call destructors on its content. we mark the import list
     * object such that its content will not be destroyed with it, since
     * we just copied that content to the return list */
    set_etchobj_static_content(inarray);

    return (objmask*) outarray;  /* caller owns the returned list */
}


/**
 * new_list_serializer() 
 * etch_serializer_list constructor
 * @param type  - not owned
 * @param field - not owned
 */
etch_serializer* new_list_serializer(etch_type* type, etch_field* field) 
{
    etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);

    newobj->class_id = CLASSID_SERIALIZER_LIST;
    newobj->type  = type;  /* not owned */
    newobj->field = field; /* not owned */

    newobj->export_value = etchserializer_list_export_value;
    newobj->import_value = etchserializer_list_import_value;

    return newobj;
}


/**
 * etch_serializer_list_init() 
 * static intitializer for list serializer.
 * creates the list impex serializer and installs it to the supplied type.
 * @param thistype type of the serializer (list)
 */
int etch_serializer_list_init(etch_type* thistype, etch_hashtable* c2tmap)
{
    const wchar_t* key_name = str_values; /* vf static constant */
    const unsigned thisclass 
        = ETCHMAKECLASS(ETCHTYPEB_ETCHLIST, CLASSID_ETCH_LIST);

    return etch_serializer_init(thistype, key_name, thisclass, c2tmap, 
        etchvtor_object_get(1), new_list_serializer);
}


/* - - - - - - - - - - - - - - - - - -
 * map serializer
 * - - - - - - - - - - - - - - - - - -  
 */

/**
 * etchserializer_map_export_value() 
 * virtual export value for etch map
 * @param objval an etch_hashtable containing values to export. caller owns it,
 * and presumably will destroy it on return from this method. this method marks
 * this object such that its destructor will not destroy its content objects, 
 * ownership of which is transferred here to the returned arraylist.
 * @return the exported structvalue object in which the lone entry is an
 * etch_arraylist containing the map keys and values as consecutive entries.
 * caller owns this returned structvalue.
 */
objmask* etchserializer_map_export_value(etch_serializer* thisx, objmask* objval) 
{
    int result = 0, mapsize = 0, listsize = 0, is_expected_maptype = 0;
    etch_iterator iterator;
    const int THISINITSIZE = 2;
    const int is_this_a_set = (thisx->type->id == builtins._mt__etch_set->id);
    etch_hashtable* impmap  = NULL;
    etch_arraylist* explist = NULL;
    etch_structvalue* expstruct = NULL;
    if (!is_etch_hashtable(objval)) return NULL;

    impmap   = (etch_hashtable*) objval;
    mapsize  = etchmap_count(impmap);
    listsize = is_this_a_set? mapsize: mapsize * 2;

    /* the underlying hashtable for an etch map must be explicitly marked 
     * as having object-key, object-value content, since the export list
     * content is object-key, object-value, object-key, object value, etc. */
    is_expected_maptype = is_this_a_set?
        impmap->content_type == ETCHHASHTABLE_CONTENT_OBJECT_NONE:
        impmap->content_type == ETCHHASHTABLE_CONTENT_OBJECT_OBJECT;
    if (!is_expected_maptype) return NULL;
   
    /* create native array for export, marked such that 
     * destructors will be called on list content */
    explist = new_synchronized_arraylist(listsize, 0);
    explist->class_id = CLASSID_ETCH_ARRAYLIST;
    explist->content_type = ETCHARRAYLIST_CONTENT_OBJECT;  
    explist->is_readonly  = FALSE; 
    explist->content_obj_type = impmap->content_obj_type;
    explist->content_class_id = impmap->content_class_id;

    set_iterator(&iterator, impmap, &impmap->iterable);

    while(iterator.has_next(&iterator))  /* copy map data to array */
    {   
        arraylist_add(explist, iterator.current_key);
        if  (is_this_a_set);   /* a set has only keys */  
        else arraylist_add(explist, iterator.current_value);
        iterator.next(&iterator);
    }

    /* mark caller's map such that it will not destroy its content, 
     * ownership of which we just transferred to the export array */
    impmap->is_readonly_keys = impmap->is_readonly_values = TRUE;
    impmap->freehook = etch_noop_clear_handler;
    
    expstruct = new_structvalue((etch_type*) thisx->type, THISINITSIZE);

    /* here we assign ownership of the new arraylist to the struct */
    result = structvalue_put(expstruct, clone_field(thisx->field), (objmask*)explist);

    return (objmask*) expstruct; /* caller owns this structvalue */
}


/**
 * etchserializer_map_import_value() 
 * virtual import value for etch map.
 * @param objval an etch_structvalue of type etch map, wrapping a
 * single entry of etch_arrayvalue, wrapping an etch_arraylist in which
 * consecutive entries represent a map key and a map value. this method
 * will mark the arraylist such that it will not destroy its object content.
 * caller retains ownership of this object as with all imports.
 * @return a disposable etch_hashtable containing the imported data objects.
 * caller must cast it and destroy it. 
 *
 * note that the c binding works a bit differently that does the java here.
 * java tdi populates the structvalue with an object[]. in c the tdi inserts a 
 * synchronized etch_arraylist wrapped by an etch_arrayvalue, to the structvalue. 
 */
objmask* etchserializer_map_import_value(etch_serializer* thisx, objmask* objval) 
{
    int i = 0, result = 0, listcount = 0, mapsize = 0;
    etch_hashtable* expmap = NULL;
    etch_arraylist* implist = NULL;
    etch_structvalue* impstruct = NULL;
    const int is_this_a_set = thisx->type->id == builtins._mt__etch_set->id;
    if (!is_etch_struct(objval)) return NULL;

    impstruct = (etch_structvalue*) objval;
    if (!structvalue_is_type(impstruct, thisx->type)) return NULL;

    /* get the synchronized arraylist out of the struct and mark the list 
     * such that it no longer owns its object content, ownership of which we
     * are transferring to the exported map. 
     */
    implist = (etch_arraylist*) structvalue_get(impstruct, thisx->field);
    if (!is_etch_arraylist(implist)) return NULL;
    set_etchobj_static_content(implist);
    listcount = implist->count;

    /* instantiate a map configured appropriately for the import data */
    /* todo code a new_synchronized_map() constructor and use it here */
    if  (is_this_a_set)
         expmap = new_etch_set(listcount);
    else expmap = new_etch_map(listcount / 2);
    expmap->content_obj_type = implist->content_obj_type;
    expmap->content_class_id = implist->content_class_id;  

    while(i < (const int) listcount) /* copy data objects to the map */ 
    {   
        objmask* key = arraylist_get(implist, i++);
        if (NULL == key) continue;
        
        if  (is_this_a_set)
             result = etchmap_set_add(expmap, key);
        else result = etchmap_map_add(expmap, key, arraylist_get(implist, i++));

        if (-1 == result)
            etchlog(ETCHIMPX, ETCHLOG_ERROR, "map insert error on key %u", key);
    } 

    return (objmask*) expmap;
}


/**
 * new_map_serializer() 
 * etch_serializer_map constructor
 * @param type  - not owned
 * @param field - not owned
 */
etch_serializer* new_map_serializer(etch_type* type, etch_field* field) 
{
    etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);

    newobj->class_id = CLASSID_SERIALIZER_MAP;
    newobj->type  = type;  /* not owned */
    newobj->field = field; /* not owned */

    newobj->export_value = etchserializer_map_export_value;
    newobj->import_value = etchserializer_map_import_value;

    return newobj;
}


/**
 * etch_serializer_map_init() 
 * static intitializer for map serializer.
 * creates the map serializer and installs it to the supplied type.
 * @param thistype type of the serializer (map)
 */
int etch_serializer_map_init(etch_type* thistype, etch_hashtable* c2tmap)
{
    const wchar_t* keyname = str_keys_values; /* vf static constant */
    const unsigned thisclass 
        = ETCHMAKECLASS(ETCHTYPEB_ETCHMAP, CLASSID_ETCH_MAP);
   
    return etch_serializer_init(thistype, keyname, thisclass, c2tmap, 
        etchvtor_object_get(1), new_map_serializer);
}


/* - - - - - - - - - - - - - - - - - -
 * set serializer
 * - - - - - - - - - - - - - - - - - -  
 */

/**
 * etchserializer_set_export_value() 
 * virtual export value for etch set
 * @param objval an etch_hashtable containing values to export. caller owns it,
 * and presumably will destroy it on return from this method. this method marks
 * the map object such that it will not destroy its content objects.
 * @return the exported structvalue object in which the lone entry is an
 * etch_arrayvalue containing the map keys
 * caller owns memory for the returned object.
 */
objmask* etchserializer_set_export_value(etch_serializer* thisx, objmask* objval) 
{
    return etchserializer_map_export_value(thisx, objval); /* caller owns */
}


/**
 * etchserializer_set_import_value() 
 * virtual import value for etch set.
 * @param objval an etch_structvalue of type etch set, wrapping a single entry
 * of etch_arrayvalue, wrapping an etch_arraylist containing the set values.
 * this method will mark the arraylist such that it will not destroy its content.
 * caller retains ownership of this object as with all imports.
 * @return a disposable etch_hashtable containing the imported data objects.
 * caller must cast it and destroy it. 
 * note that the c binding works a bit differently that does the java here.
 * java tdi populates the structvalue with an object[]. in c the tdi inserts a 
 * synchronized etch_arraylist wrapped by an etch_arrayvalue, to the structvalue. 
 */
objmask* etchserializer_set_import_value(etch_serializer* thisx, objmask* objval) 
{
    return etchserializer_map_import_value(thisx, objval); 
}


/**
 * new_set_serializer() 
 * etch_serializer_set constructor
 * @param type  - not owned
 * @param field - not owned
 */
etch_serializer* new_set_serializer(etch_type* type, etch_field* field) 
{
    etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);

    newobj->class_id = CLASSID_SERIALIZER_SET;
    newobj->type  = type;  /* not owned */
    newobj->field = field; /* not owned */

    newobj->export_value = etchserializer_set_export_value;
    newobj->import_value = etchserializer_set_import_value;

    return newobj;
}


/**
 * etch_serializer_set_init() 
 * static intitializer for set serializer.
 * creates the set serializer and installs it to the supplied type.
 * @param thistype type of the serializer (set)
 */
int etch_serializer_set_init(etch_type* thistype, etch_hashtable* c2tmap)
{
    const wchar_t* keyname = str_keys; /* vf static constant */
    const unsigned thisclass 
        = ETCHMAKECLASS(ETCHTYPEB_HASHTABLE, CLASSID_ETCH_SET);
       
    return etch_serializer_init(thistype, keyname, thisclass, c2tmap, 
        etchvtor_object_get(1), new_set_serializer);
}


/* - - - - - - - - - - - - - - - - - -
 * date serializer
 * - - - - - - - - - - - - - - - - - -  
 */

/**
 * etchserializer_date_export_value() 
 * virtual export value for date
 * @param objval an etch_date object for export to tdo. 
 * caller retains ownership of this object. 
 * @return the exported structvalue object. caller owns it and must cast it.
 */
objmask* etchserializer_date_export_value(etch_serializer* thisx, objmask* objval) 
{
    const int THISINITSIZE = 2;
    etch_structvalue* expstruct = NULL;
    if (!is_etch_date(objval)) return NULL;

    expstruct = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
    
    /* assign ownership of the wrapped 64-bit integer to the struct */
    structvalue_put(expstruct, clone_field(thisx->field), 
       (objmask*) new_int64(((etch_date*)objval)->value));

    return (objmask*) expstruct; /* caller owns this structvalue */
}


/**
 * etchserializer_date_import_value() 
 * virtual import value for etch date.
 * @param objval an etch_structvalue of type etch date.
 * caller retains ownership of this object as with all imports.
 * @return a *disposable* etch_date object
 */
objmask* etchserializer_date_import_value(etch_serializer* thisx, objmask* objval) 
{
    etch_date* outdate = NULL;
    etch_int64* timeval = NULL;
    etch_structvalue* instruct = NULL;
    if (!is_etch_struct(objval)) return NULL;

    instruct = (etch_structvalue*) objval;
    if (!structvalue_is_type(instruct, thisx->type)) return NULL;

    /* fetch the arrayvalue out of the struct. struct owns it */
    timeval = (etch_int64*) structvalue_get(instruct, thisx->field);
    if (!is_etch_int64(timeval)) return NULL;

    outdate = new_date();
    outdate->value = (time_t) timeval->value;

    return (objmask*) outdate;  /* caller owns this object */
}


/**
 * new_date_serializer() 
 * etch_serializer_date constructor
 * @param type  - not owned
 * @param field - not owned
 */
etch_serializer* new_date_serializer(etch_type* type, etch_field* field) 
{
    etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);

    newobj->class_id = CLASSID_SERIALIZER_DATE;
    newobj->type  = type;  /* not owned */
    newobj->field = field; /* not owned */

    newobj->export_value = etchserializer_date_export_value;
    newobj->import_value = etchserializer_date_import_value;

    return newobj;
}


/**
 * etch_serializer_date_init() 
 * static intitializer for date serializer.
 * creates the date serializer and installs it to the supplied type.
 * @param thistype type of the serializer (date)
 */
int etch_serializer_date_init(etch_type* thistype, etch_hashtable* c2tmap)
{
    const wchar_t* keyname = str_date_time; /* vf static constant */
    const unsigned thisclass 
        = ETCHMAKECLASS(ETCHTYPEB_PRIMITIVE, CLASSID_DATE);
          
    return etch_serializer_init(thistype, keyname, thisclass, c2tmap, 
        etchvtor_int64_get(0), new_date_serializer);
}


/* - - - - - - - - - - - - - - - - - -
 * misc  
 * - - - - - - - - - - - - - - - - - -  
 */

 objmask* etchserializer_defexportval(etch_serializer* s, objmask* x)
{
    return NULL;
}

objmask* etchserializer_defimportval (etch_serializer* s, objmask* x)
{
    return NULL;
}

