blob: e6c30ee5f7c8d2c5d4c719472d7f71f81b734feb [file] [log] [blame]
/* $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.
*/
/*
* type.c -- methods on the etch_type object.
* type denotes the type of a struct or message. when used with a message
* it typically denotes an action or event. an etch_type is a typedef of
* etch_id_name, however it has additional instance data specific to type.
*/
#include "etch_type.h"
#include "etch_validator.h"
#include "etch_map.h"
#include "etch_serializer.h"
#include "etch_objecttypes.h"
#include "etch_general.h"
int fieldmap_clear_handler (void* key, void* value);
etch_hashtable* new_etchtype_fieldmap();
etch_hashtable* new_etchtype_vtormap();
/* - - - - - - - - - - - - - - - - - -
* constructors and destructors
* - - - - - - - - - - - - - - - - - -
*/
/**
* destroy_type()
* etch_type destructor
*/
int destroy_type(void* data)
{
etch_type* type = (etch_type*)data;
if (!is_etchobj_static_content(type)) /* e.g., not cloned */
{
if (type->impl)
{ etch_type_impl* impl = (etch_type_impl*) type->impl;
etch_object_destroy(impl);
type->impl = NULL;
}
}
return destroy_id_name(type);
}
/**
* clone_type()
* etch_type quasi copy constructor
* originally, type was simply a name and id. now a type has considerable extra
* content. we do not clone that content here, but rather copy its reference
* from the source and mark the clone as having non-disposable content such that
* the type destructor won't try to free it. so the object is not a true clone.
* note also that if the original were to be destroyed prior to the clone, the
* clone's content memory reference would be hosed; thus we must ensure that we
* only clone static types, i.e. types which are instantiated with the service,
* (or in the case of unit tests, emulated as such), and destroyed only at
* service teardown.
*
* note finally that etch_type and etch_idname still have the same footprint,
* with the type instantiating its extra content at the impl* of the id_name.
*/
void* clone_type(void* data)
{
const etch_type* type = (const etch_type*)data;
etch_type* newtype = clone_id_name((etch_type*)type);
newtype->impl = type->impl;
set_etchobj_static_content(newtype);
return newtype;
}
/**
* destroy_type_impl()
* etch_type_impl destructor
*/
int destroy_type_impl(void* data)
{
etch_type_impl* impl = (etch_type_impl*)data;
if (!is_etchobj_static_content(impl))
{ /* destruction of the maps causes all fields and non-cached validator
* objects, to be destroyed. see etchtype_fieldmap_clear_handler,
* and etchtype_vtormap_clear_handler, below. */
etch_object_destroy(impl->vtormap);
etch_object_destroy(impl->fieldmap);
etch_object_destroy(impl->impexphelper);
}
return destroy_objectex((etch_object*)impl);
}
/**
* new_type()
* etch_type constructor
*/
etch_type* new_type(const wchar_t* name)
{
etch_type_impl* impl = NULL;
etchparentinfo* inheritlist = NULL;
etch_type* newtype = new_id_name(name);
if (NULL == newtype) return NULL;
((etch_object*)newtype)->obj_type = ETCHTYPEB_TYPE;
((etch_object*)newtype)->class_id = CLASSID_ID_TYPE;
/* ensure parent type keys exist in (one-based) inheritance list */
inheritlist = get_vtab_inheritance_list((etch_object*)newtype,
2, 1, CLASSID_VTAB_FIELD);
inheritlist[1].o.obj_type = ETCHTYPEB_ID_NAME;
inheritlist[1].c.class_id = CLASSID_ID_NAME;
/* instantiate instance data */
impl = (etch_type_impl*) new_object(sizeof(etch_type_impl),
ETCHTYPEB_IDNAMEIMPL, CLASSID_TYPEIMPL);
((etch_object*)impl)->destroy = destroy_type_impl;
impl->async_mode = ETCH_ASYNCMODE_NONE; /* where is this default reset? */
impl->fieldmap = new_etchtype_fieldmap();
impl->vtormap = new_etchtype_vtormap();
newtype->impl = (etch_object*) impl;
((etch_object*)newtype)->destroy = destroy_type;
((etch_object*)newtype)->clone = clone_type;
return newtype;
}
/**
* new__static_type()
* create a type object whose destructor will have no effect.
*/
etch_type* new_static_type(const wchar_t* name)
{
etch_type* newtype = new_type(name);
set_etchobj_static_all(newtype);
return newtype;
}
/**
* destroy_static_type()
* etch_type destructor.
* this should not be set as the virtual dtor for a type, since the type
* would then not be quasi-static as desired. it should be invoked explicitly
*/
int destroy_static_type(etch_type* type)
{
clear_etchobj_static_all(type);
return destroy_type(type);
}
/* - - - - - - - - - - - - - - - - - -
* get/set
* - - - - - - - - - - - - - - - - - -
*/
/* mutators are implemented only for instance data specific to type, i.e.
* resident in the etch_type_impl, following the c binding convention that we
* implement a mutator only when the datum can't safely be get/set directly.
*/
/**
* etchtype_set_type_stubhelper()
* set type's 'stub helper', returning existing helper if any.
* the stub helper is a function pointer, not an object.
*/
opaque_stubhelper etchtype_set_type_stubhelper(etch_type* type, opaque_stubhelper helper)
{
/* sets a callback from message type to the particular API method implementation.
* note that in the the java binding this is indirect, that is, this callback
* calls a wrapper method which calls the implementation. however in our case,
* the methods have same signature, so we call the implementation directly.
* keep an eye on this though in case I have missed something.
*/
opaque_stubhelper oldhelper = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl)
{ oldhelper = impl->stubhelper;
impl->stubhelper = helper;
}
return oldhelper;
}
/**
* etchtype_get_type_stubhelper()
* getter for stub helper - see comments at set_type_stubhelper()
*/
opaque_stubhelper etchtype_get_type_stubhelper(etch_type* type)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return impl? impl->stubhelper: NULL;
}
/**
* etchtype_set_result_type()
* set result type, returning existing result type if any.
* @param type a non-disposable reference to a type.
* todo: if result type is set only at construction, lose this.
*/
etch_type* etchtype_set_result_type(etch_type* type, etch_type* rtype)
{
etch_type* oldtype = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl)
{ oldtype = impl->result_type;
impl->result_type = rtype;
}
return oldtype;
}
/**
* etchtype_get_result_type()
* returns a non-disposable reference to result type
*/
etch_type* etchtype_get_result_type(etch_type* type)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return impl? impl->result_type: NULL;
}
/**
* etchtype_set_super_type()
* todo: if set only at construction, lose this.
* @param a non-disposable type
* @return the previous type, not disposable.
*/
etch_type* etchtype_set_super_type(etch_type* type, etch_type* stype)
{
etch_type* oldtype = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl)
{ oldtype = impl->super_type;
impl->super_type = stype;
}
return oldtype;
}
/**
* etchtype_get_super_type()
* returns a non-disposable reference to super type
*/
etch_type* etchtype_get_super_type(etch_type* type)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return impl? impl->super_type: NULL;
}
/**
* etchtype_set_component_type()
* set associated component type and class for an array of this class.
* @param type a non-disposable type object. neither owned nor stored here.
* @return the existing objtype/classid representing component type.
*/
unsigned int etchtype_set_component_type(etch_type* type, unsigned int typeclass)
{
unsigned int old_class = 0;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl)
{ old_class = impl->component_class;
impl->component_class = typeclass;
}
return old_class;
}
/**
* etchtype_get_component_type()
* returns component obj_type and class_id or zero indicating none set.
*/
unsigned int etchtype_get_component_type(etch_type* type)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return impl? impl->component_class: 0;
}
/**
* etchtype_set_async_mode()
* set async_mode, which determines if requests are run on the queued or
* free thread pool.
*/
unsigned char etchtype_set_async_mode (etch_type* type, unsigned char mode)
{
unsigned char old_mode = 0;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl)
{ old_mode = impl->async_mode;
impl->async_mode = mode;
}
return old_mode;
}
/**
* etchtype_get_async_mode()
* returns component async_mode
*/
unsigned char etchtype_get_async_mode (etch_type* type)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return impl? impl->async_mode: 0;
}
/**
* etchtype_set_timeout()
* set timeout to wait for response, in milliseconds.
*/
unsigned int etchtype_set_timeout(etch_type* type, unsigned int ms)
{
unsigned int old_timeout = 0;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl)
{ old_timeout = impl->timeout;
impl->timeout = ms;
}
return old_timeout;
}
/**
* etchtype_get_timeout()
* get timeout to wait for response, in milliseconds.
*/
unsigned int etchtype_get_timeout(etch_type* type)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return impl? impl->timeout: 0;
}
/**
* etchtype_set_run_validators()
* set boolean run validators flag, returning existing value.
*/
unsigned char etchtype_set_run_validators(etch_type* type, unsigned char val)
{
unsigned char old_flag = 0;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl)
{ old_flag = impl->is_run_validators;
impl->is_run_validators = val;
}
return old_flag;
}
/**
* etchtype_get_response_field()
*/
etch_field* etchtype_get_response_field(etch_type* type)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return impl? impl->response_field: NULL;
}
/**
* etchtype_set_response_field()
*/
etch_field* etchtype_set_response_field(etch_type* type, etch_field* field)
{
etch_field* old_field = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl)
{ old_field = impl->response_field;
impl->response_field = field;
}
return old_field;
}
/**
* etchtype_get_run_validators()
* get boolean run validators flag.
*/
unsigned char etchtype_get_run_validators(etch_type* type)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return impl? impl->is_run_validators: 0;
}
/**
* etchtype_set_impexphelper()
* setter for import/export helper object
* @param helper the helper object to be assigned, or null.
* caller relinquishes ownership of this object if present.
* @return the *disposable* prior helper object if any.
* if present, caller now owns this object.
*/
etch_serializer* etchtype_set_impexphelper(etch_type* type, etch_serializer* helper)
{
etch_serializer* oldhelper = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl)
{ oldhelper = impl->impexphelper;
impl->impexphelper = helper;
}
return oldhelper;
}
/**
* etchtype_get_impexphelper()
* getter for import/export helper object
* @return a *non-disposable* reference to helper object.
* the type retains ownership of this object.
*/
etch_serializer* etchtype_get_impexphelper(etch_type* type)
{
etch_type_impl* impl = type? (etch_type_impl*) type->impl: NULL;
return impl? impl->impexphelper: NULL;
}
/**
* etchtype_is_assignable_from()
* indicate if this type is assignable from other, i.e. other a subclass of this
*/
int etchtype_is_assignable_from(etch_type* type, etch_type* othertype)
{
etch_type* other_supertype;
if (NULL == othertype) return FALSE;
if (is_equal_types(type, othertype)) return TRUE;
other_supertype = othertype->impl?
((etch_type_impl*)othertype->impl)->super_type: NULL;
return etchtype_is_assignable_from(type, other_supertype);
}
/* - - - - - - - - - - - - - - - - -
* fieldmap accessors
* - - - - - - - - - - - - - - - - -
*/
/**
* get_key_by_name()
* look up an etch_id_name key object by a name string. recall that etch_type
* and etch_field each are typedefs of etch_id_name. id_name derivations are
* keyed by hash of name, so lookup is direct;
*/
etch_id_name* etchtype_get_key_by_name(etch_hashtable* map, const wchar_t* name)
{
etch_hashitem hashbucket, *thisitem = &hashbucket;
const unsigned hashkey = etch_get_wchar_hashkey(name);
const int result = ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, hashkey, map, (void**)&thisitem);
return result == 0? (etch_id_name*) thisitem->key: NULL;
}
/**
* get_idname_by_id()
* given a hashtable and an id_name "id", return the map's id_name key having that id.
* note that a non-disposable *reference* is returned, not a copy.
*/
etch_id_name* etchtype_get_key_by_id (etch_hashtable* map, const unsigned id)
{
etch_iterator iterator;
hashtable_getlock(map);
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) {
hashtable_rellock(map);
return this_idname;
}
iterator.next(&iterator);
}
hashtable_rellock(map);
return NULL;
}
/**
* etchtype_add_field()
* adds a field to set of fields
* @param field caller must supply a disposable field object.
* @return the argument. If there is a name collision, the existing field
* is returned in place of the supplied field, AND the supplied field is
* DESTROYED. this simplifies logic up the line, and is consistent with caller
* expecting to relinquish responsibility for the field passed.
*/
etch_field* etchtype_add_field (etch_type* type, etch_field* field)
{
etch_field *effective_field = field;
etch_type_impl* impl = (etch_type_impl*) type->impl;
etch_hashtable* map = impl->fieldmap;
int result = ((struct i_hashtable*)((etch_object*)impl->fieldmap)->vtab)->inserth
(map->realtable, field, NULL, map, 0);
if (-1 == result)
effective_field = etchtype_get_field_by_name(type, field->name);
if (effective_field != field)
etch_object_destroy(field);
#ifdef ETCHTYPE_DEBUG
if (effective_field)
wprintf(L"add field %08x '%s'\n",
(size_t) (void*) effective_field, effective_field->name);
else wprintf(L"error adding field '%s'\n", field->name);
#endif
return effective_field;
}
/**
* etchtype_get_field_by_id()
* @return a non-disposable reference to the requested field, or null
*/
etch_field* etchtype_get_field_by_id (etch_type* type, const unsigned id)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return etchtype_get_key_by_id(impl->fieldmap, id);
}
/**
* etchtype_get_field_by_name()
* works as in the java binding, in that if the type does not include
* a field with that name, a new field is created and added to the type.
* @return a non-disposable reference to the requested field, or null
*/
etch_field* etchtype_get_field_by_name (etch_type* type, const wchar_t* name)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
etch_field* field = etchtype_get_key_by_name(impl->fieldmap, name);
if (NULL == field)
field = etchtype_add_field (type, new_field(name));
return field;
}
/**
* etchtype_get_fields()
* returns a disposable arraylist of references. the list is marked
* such that list->destroy() will not attempt to free content.
* caller must cast result to etch_arraylist*
*/
void* etchtype_get_fields (etch_type* type)
{
etch_type_impl* impl = (etch_type_impl*) type->impl;
return get_map_keys(impl->fieldmap);
}
/*
* etchtype_fields_count()
* return count of fields resident in the type.
*/
int etchtype_fields_count(etch_type* type)
{
int count = 0;
etch_hashtable* map = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl) map = impl->fieldmap;
if (map) count = ((struct i_hashtable*)((etch_object*)map)->vtab)->count(map->realtable, 0, 0);
return count;
}
/*
* etchtype_set_fields_iterator()
* initialize iterator over fields.
*/
int etchtype_set_fields_iterator(etch_type* type, etch_iterator* iterator)
{
etch_hashtable* map = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl) map = impl->fieldmap;
return map? set_iterator(iterator, map, &map->iterable): -1;
}
/* - - - - - - - - - - - - - - - - - - -
* validator insert/lookup
* - - - - - - - - - - - - - - - - - - -
*/
/**
* etchtype_get_validator_by_id()
* caller will want to cast result to etch_validator*.
* note that the etch_type header can't include etch_validator header,
* thus the anonymous pointers to etch_validator in these methods.
*/
etch_object* etchtype_get_validator_by_id (etch_type* type, const unsigned id)
{
etch_hashtable* map;
etch_iterator iterator;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (!impl || !id) return NULL;
map = impl->vtormap;
set_iterator(&iterator, map, &map->iterable);
while(iterator.has_next(&iterator))
{
etch_field* this_field = (etch_field*) iterator.current_key;
if (this_field->id == id)
return iterator.current_value;
iterator.next(&iterator);
}
return NULL;
}
/**
* etchtype_get_validator_by_name()
* @param name the name of the etch_field keying the validator.
* caller will want to cast result to etch_validator*
*/
etch_object* etchtype_get_validator_by_name (etch_type* type, const wchar_t* name)
{
etch_type_impl* impl = type? (etch_type_impl*) type->impl: NULL;
if (impl && name)
{
etch_hashitem hashbucket, *thisitem = &hashbucket;
etch_hashtable* map = impl->vtormap;
const unsigned key = etch_get_wchar_hashkey(name);
if (0 == ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, key, map, (void**)&thisitem))
return thisitem->value;
}
return NULL;
}
/**
* etchtype_put_validator()
* adds a validator to validator chain for specified key
* @param field relinquished regardless of result.
* @param new_vtor relinquished regardless of result.
*/
int etchtype_put_validator (etch_type* type, etch_field* field, etch_object* new_vtor)
{
int result = -1;
etch_hashtable *fmap, *vmap;
etch_hashitem hashbucket, *mapentry = &hashbucket;
etch_type_impl* impl = (etch_type_impl*) type->impl;
ETCH_ASSERT(impl);
do /* validate parameters */
{
if (!is_etch_validator(new_vtor)) {
etch_object_destroy(field);
field = NULL;
break;
}
if (NULL == field) {
etch_object_destroy(new_vtor);
new_vtor = NULL;
break;
}
result = 0;
} while(0);
if (0 != result) return result;
if (!impl || !is_etch_validator(new_vtor) || !field) return -1;
memset(mapentry, 0, sizeof(etch_hashitem));
fmap = impl->fieldmap;
vmap = impl->vtormap;
/* add field to fieldmap. if an eponymous field was present, it is returned
* in place of caller's field, and caller's field has been destroyed. */
field = etchtype_add_field (type, field);
/* if a validator exists under the specified key, we'll chain the new
* validator to the existing validator with a new combo validator object,
* and replace the current vtor map entry with the new combo validator.
*/
result = ((struct i_hashtable*)((etch_object*)vmap)->vtab)->removeh /* if already in vtormap, remove it */
(vmap->realtable, ((etch_object*)field)->get_hashkey(field), vmap, (void**)&mapentry);
if (result == -1) /* if it was not already in vtormap, insert it */
result = ((struct i_hashtable*)((etch_object*)vmap)->vtab)->inserth (vmap->realtable,
etch_object_clone_func(field), new_vtor, vmap, 0);
else /* ... otherwise insert a new chain head */
{ etch_field* existing_key = (etch_field*) mapentry->key;
etch_validator* existing_vtor = (etch_validator*) mapentry->value;
etch_validator* vcombo /* chain new vtor to existing vtor ... */
= new_combo_validator(existing_vtor, (etch_validator*) new_vtor);
result = ((struct i_hashtable*)((etch_object*)vmap)->vtab)->inserth /* ... and insert the new chained vtor */
(vmap->realtable, existing_key, vcombo, vmap, 0);
}
return result;
}
/*
* etchtype_clear_validator()
* remove the validator chain for specified key.
*/
int etchtype_clear_validator(etch_type* type, etch_field* key)
{
int result = -1;
etch_hashtable* map = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl) map = impl->vtormap;
if (map)
{ etch_validator* removed_vtor = NULL;
etch_hashitem hashbucket, *mapentry = &hashbucket;
memset(mapentry, 0, sizeof(etch_hashitem));
result = ((struct i_hashtable*)((etch_object*)map)->vtab)->removeh(map->realtable, ((etch_object*)key)->get_hashkey(key), map, (void**)&mapentry);
if (result == 0) /* returned content is head of validator chain */ {
removed_vtor = (etch_validator*) mapentry->value;
destroy_type((etch_type*) mapentry->key);
}
if (removed_vtor) /* destructors are called up the chain */
etch_object_destroy(removed_vtor);
}
return 0;
}
/*
* etchtype_clear_validators()
* clear all type validators. non-cached validators are destroyed.
* returns count of items cleared, or -1 if error.
*/
int etchtype_clear_validators(etch_type* type)
{
int result = -1;
etch_hashtable* map = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl) map = impl->vtormap;
if (map) /* we ask hashtable clear() to call content destructors */
result = ((struct i_hashtable*)((etch_object*)map)->vtab)->clear(map->realtable, FALSE, TRUE, map, 0);
return result;
}
/*
* etchtype_validators_count()
* return count of validators in chain.
*/
int etchtype_validators_count(etch_type* type)
{
int count = 0;
etch_hashtable* map = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl) map = impl->vtormap;
if (map) count = ((struct i_hashtable*)((etch_object*)map)->vtab)->count(map->realtable, 0, 0);
return count;
}
/*
* etchtype_set_validators_iterator()
* initialize iterator over validators.
*/
int etchtype_set_validators_iterator(etch_type* type, etch_iterator* iterator)
{
etch_hashtable* map = NULL;
etch_type_impl* impl = (etch_type_impl*) type->impl;
if (impl) map = impl->vtormap;
return map? set_iterator(iterator, map, &map->iterable): -1;
}
/* - - - - - - - - - - - - - - - - - -
* utility methods
* - - - - - - - - - - - - - - - - - -
*/
/*
* etchtype_fieldmap_clear_handler()
* callback set to handle freeing of key memory during a clear() of the fields
* map. note that this map is used as a quasi set, so the value is always null.
* the etch_fields are owned by the map and destroyed as the map is destroyed.
* handlers return FALSE to indicate memory free NOT handled.
*/
int etchtype_fieldmap_clear_handler (void* key, void* value)
{
((etch_object*)key)->destroy(key);
return TRUE;
}
/**
* new_etchtype_fieldmap()
* construct and return a hashtable configured as expected for the fieldmap
*/
etch_hashtable* new_etchtype_fieldmap()
{
etch_hashtable* map = new_hashtable_synchronized(ETCHTYPE_DEFSIZE_FIELDMAP);
if (NULL == map) return NULL;
map->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
map->is_tracked_memory = TRUE;
map->is_readonly_keys = FALSE; /* keys are disposable field objects */
map->is_readonly_values = FALSE; /* value is always null */
map->freehook = etchtype_fieldmap_clear_handler;
return map;
}
/*
* etchtype_vtormap_clear_handler()
* callback set to handle freeing of field and validator memory
* during a clear() of the validators map.
*/
int etchtype_vtormap_clear_handler (void* key, void* value)
{
/* note that value->destroy() is invoking the validator destructor,
* and that this will have no effect on a validator marked as cached */
etch_object_destroy(value);
etch_object_destroy(key);
return TRUE;
}
/**
* new_etchtype_vtormap()
* construct and return a hashtable configured as expected for validators.
* this is a map of non-disposable etch_field keys to etch_validator*
* object references. the validators are considered as disposable, in that
* clearing the map will result in validator destructor calls; however
* note that destroy() has no effect on a cached validator - these are
* not destroyed until such time as the validator cache is cleared.
*/
etch_hashtable* new_etchtype_vtormap()
{
etch_hashtable* map = new_hashtable_synchronized(ETCHTYPE_DEFSIZE_VTORMAP);
if (NULL == map) return NULL;
map->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
map->is_tracked_memory = TRUE;
map->is_readonly_keys = TRUE; /* keys are nondisposable field objects */
map->is_readonly_values = FALSE; /* values are validator function pointers */
map->freehook = etchtype_vtormap_clear_handler;
return map;
}