/* Subroutines and data structures for storing "subtypes". */ /* */ /* A subtype is, for example, a list of TILES. This can be interpreted */ /* as an additional axis like the vertical axis. */ /* */ /* @author 02/2015 F. Prill, DWD */ /* */ /* DATA LAYOUT: */ /* */ /* A subtype contains several "subtype entries", each of which */ /* contains a linked list of subtype attributes. */ /* */ /* The number of subtype entries is not specified in advance, but the */ /* list of entries is itself dynamically growing. There is no */ /* guaranteed ordering of the entries, therefore each entry must be */ /* identifiable by its attributes. */ /* */ /* [subtype_t] */ /* | */ /* |------- globals [subtype_entry_t] */ /* | |--- atts [subtype_attr_t] */ /* | */ /* |------- entries */ /* |- entry #0 */ /* | |--- atts [subtype_attr_t] */ /* |- entry #1 */ /* | |--- atts [subtype_attr_t] */ /* |- entry #2 */ /* . |--- atts [subtype_attr_t] */ /* . */ #if defined (HAVE_CONFIG_H) # include "config.h" #endif #include "cdi.h" #include "cdi_int.h" #include "dmemory.h" #include "subtype.h" #include "vlist.h" /* Literal constants corresponding to the different subtypes of the enumeration "subtype_kind". */ static const char* subtypeName[] = { "tileset" }; const char * const cdiSubtypeAttributeName[] = { "tileIndex", "totalNumberOfTileAttributePairs", "tileClassification", "numberOfTiles", "numberOfTileAttributes", "tileAttribute" }; /* prototypes: */ static int subtypeCompareP (subtype_t *z1, subtype_t *z2); static void subtypeDestroyP ( void * subtype_ptr ); static void subtypePrintP ( void * subtype_ptr, FILE * fp ); static int subtypeGetPackSize ( void * subtype_ptr, void *context); static void subtypePack ( void * subtype_ptr, void * buffer, int size, int *pos, void *context); static int subtypeTxCode ( void ); static const resOps subtypeOps = { (int (*) (void *, void *)) subtypeCompareP, (void (*)(void *)) subtypeDestroyP, (void (*)(void *, FILE *)) subtypePrintP, (int (*) (void *, void *)) subtypeGetPackSize, subtypePack, subtypeTxCode }; enum { differ = 1, }; /* ------------------------------------------------------------------- */ /* SUBROUTINES FOR ATTRIBUTE LISTS */ /* ------------------------------------------------------------------- */ static int attribute_to_index(const char *key) { if (key == NULL) Error("Internal error!"); for (int i=0; ikey = key; ptr->val = val; ptr->next = NULL; head->atts = ptr; return ptr; } /* @Function subtypeAttrInsert @Title Add subtype attribute to linked list, s.t. the result is a smallest-to-largest ordered list. @EndFunction */ static struct subtype_attr_t* subtypeAttrInsert(struct subtype_entry_t* head, int key, int val) { if (head == NULL) Error("Internal error!"); if (head->atts == NULL) return (subtypeAttrNewList(head, key, val)); /* create new attribute */ struct subtype_attr_t* ptr = (struct subtype_attr_t*) Malloc(sizeof(struct subtype_attr_t)); if(NULL == ptr) Error("Node creation failed"); ptr->key = key; ptr->val = val; ptr->next = NULL; /* find the right place for insertion: */ if (head->atts->key >= key) { /* insert at position 0 */ ptr->next = head->atts; head->atts = ptr; } else { struct subtype_attr_t** predec = &head->atts; while (((*predec)->next != NULL) && ((*predec)->next->key < key)) { predec = &((*predec)->next); } ptr->next = (*predec)->next; (*predec)->next = ptr; } return ptr; } /* Recursively free a linked list with attributes. */ static void subtypeAttrDestroy(struct subtype_attr_t* head) { if (head == NULL) return; subtypeAttrDestroy(head->next); Free(head); head = NULL; } /* Find an attribute in linked list by its key or return NULL otherwise. */ static struct subtype_attr_t* subtypeAttrFind(struct subtype_attr_t* head, int key) { if (head == NULL) return NULL; else if (head->key == key) return head; else return subtypeAttrFind(head->next, key); } /* Recursively compares two subtype attribute lists under the implicit assumptions that both lists are ordered by their keys and that keys are unique. */ static int subtypeAttsCompare(struct subtype_attr_t *a1, struct subtype_attr_t *a2) { if ((a1 == NULL) && (a2 == NULL)) return 0; else if ((a1 == NULL) && (a2 != NULL)) { return differ; } else if ((a1 != NULL) && (a2 == NULL)) { return differ; } if (a1->key != a2->key) { return differ; } if (a1->val != a2->val) return differ; return subtypeAttsCompare(a1->next, a2->next); } /* (Recursively) duplicate linked list of attributes. */ static void subtypeAttsDuplicate(struct subtype_attr_t *a1, struct subtype_entry_t* dst) { if (a1 == NULL) return; /* duplicate "a1->key", "a1->val" */ subtypeAttsDuplicate(a1->next, dst); (void) subtypeAttrInsert(dst, a1->key, a1->val); } /* ------------------------------------------------------------------- */ /* SUBROUTINES FOR LIST OF ENTRIES */ /* ------------------------------------------------------------------- */ /* @Function subtypeEntryNewList @Title Create new linked list of subtype entries. @EndFunction */ static struct subtype_entry_t* subtypeEntryNewList(subtype_t* head) { struct subtype_entry_t *ptr = (struct subtype_entry_t*) Malloc(sizeof(struct subtype_entry_t)); if(NULL == ptr) Error("Node creation failed"); ptr->atts = NULL; ptr->next = NULL; head->entries = ptr; head->nentries = 0; ptr->self = head->nentries++; return ptr; } /* @Function subtypeEntryInsert @Title Add subtype entry to the head of a linked list. @EndFunction */ struct subtype_entry_t* subtypeEntryInsert(subtype_t* head) { if (head == NULL) Error("Internal error!"); if (head->entries == NULL) return (subtypeEntryNewList(head)); /* create new entry */ struct subtype_entry_t* ptr = (struct subtype_entry_t*) Malloc(sizeof(struct subtype_entry_t)); if(NULL == ptr) Error("Node creation failed"); ptr->atts = NULL; ptr->self = head->nentries++; /* find the right place for insertion: */ if (head->entries->self >= ptr->self) { /* insert at position 0 */ ptr->next = head->entries; head->entries = ptr; } else { struct subtype_entry_t** predec = &head->entries; while (((*predec)->next != NULL) && ((*predec)->next->self < ptr->self)) { predec = &((*predec)->next); } ptr->next = (*predec)->next; (*predec)->next = ptr; } return ptr; } /* @Function subtypeEntryAppend @Title Append subtype entry to the end of a linked list. @EndFunction */ static struct subtype_entry_t* subtypeEntryAppend(subtype_t* head) { if (head == NULL) Error("Internal error!"); if (head->entries == NULL) return (subtypeEntryNewList(head)); /* create new entry */ struct subtype_entry_t* ptr = (struct subtype_entry_t*) Malloc(sizeof(struct subtype_entry_t)); if(NULL == ptr) Error("Node creation failed"); ptr->atts = NULL; ptr->next = NULL; ptr->self = head->nentries++; /* find last position of linked list */ struct subtype_entry_t* prec_ptr = head->entries; while (prec_ptr->next != NULL) prec_ptr = prec_ptr->next; prec_ptr->next = ptr; return ptr; } /* Recursively free a list of subtype entries. */ static void subtypeEntryDestroy(struct subtype_entry_t *entry) { if (entry == NULL) return; subtypeEntryDestroy(entry->next); subtypeAttrDestroy(entry->atts); Free(entry); entry = NULL; } /* Compares two subtype entries. */ static int subtypeEntryCompare(struct subtype_entry_t *e1, struct subtype_entry_t *e2) { if (e1 == NULL) Error("Internal error!"); if (e2 == NULL) Error("Internal error!"); return (e1->self == e2->self) && subtypeAttsCompare(e1->atts, e2->atts); } /* (Recursively) duplicate list of entries. */ static void subtypeEntryDuplicate(struct subtype_entry_t *a1, subtype_t* dst) { if (a1 == NULL) return; /* append entry to dst pointer */ struct subtype_entry_t *ptr = subtypeEntryAppend(dst); /* duplicate attributes */ subtypeAttsDuplicate(a1->atts, ptr); ptr->self = a1->self; /* call next link in linked list */ subtypeEntryDuplicate(a1->next, dst); } /* ------------------------------------------------------------------- */ /* SUBROUTINES FOR THE SUBTYPE ITSELF */ /* ------------------------------------------------------------------- */ /* Print-out subtype data structure together with its attributes. */ static void subtypePrintKernel(subtype_t *subtype_ptr, FILE *fp) { if (subtype_ptr == NULL) Error("Internal error!"); fprintf(fp, "# %s (subtype ID %d)\n", subtypeName[subtype_ptr->subtype], subtype_ptr->self); /* print global attributes of this subtype */ struct subtype_attr_t* ptr = subtype_ptr->globals.atts; if (ptr != NULL) fprintf(fp, "#\n# global attributes:\n"); while (ptr != NULL) { fprintf(fp, "# %-40s (%2d) : %d\n", cdiSubtypeAttributeName[ptr->key], ptr->key, ptr->val); ptr = ptr->next; } /* print attributes for each subtype */ fprintf(fp, "# %d local entries:\n", subtype_ptr->nentries); struct subtype_entry_t *entry = subtype_ptr->entries; while (entry != NULL) { fprintf(fp, "# subtype entry %d\n", entry->self); ptr = entry->atts; if (ptr != NULL) fprintf(fp, "# attributes:\n"); while (ptr != NULL) { fprintf(fp, "# %-40s (%2d) : %d\n", cdiSubtypeAttributeName[ptr->key], ptr->key, ptr->val); ptr = ptr->next; } entry = entry->next; } fprintf(fp, "\n"); } /* Compares two subtype data structures. Pointer version of this method. */ static int subtypeCompareP(subtype_t *s1, subtype_t *s2) { xassert(s1 && s2); if (s1->subtype != s2->subtype) return differ; if (subtypeEntryCompare(&s1->globals, &s2->globals) != 0) return differ; struct subtype_entry_t *entry1 = s1->entries; struct subtype_entry_t *entry2 = s2->entries; while ((entry1 != NULL) && (entry2 != NULL)) { if (subtypeEntryCompare(entry1, entry2) != 0) return differ; entry1 = entry1->next; entry2 = entry2->next; } /* compare list lengths: */ if ((entry1 != NULL) || (entry2 != NULL)) return differ; return 0; } /* Clean up data structure. */ static void subtypeDestroyP(void *ptr) { subtype_t *subtype_ptr = (subtype_t*) ptr; /* destroy global attributes */ subtypeAttrDestroy(subtype_ptr->globals.atts); /* destroy list of subtype entries */ subtypeEntryDestroy(subtype_ptr->entries); subtype_ptr->entries = NULL; Free(subtype_ptr); subtype_ptr = NULL; } /* Non-static wrapper function for "subtypeDestroyP". */ void subtypeDestroyPtr(void *ptr) { subtypeDestroyP(ptr); } /* Non-static wrapper function for "subtypeCompareP". */ int subtypeComparePtr(int s1_ID, subtype_t *s2) { subtype_t *subtype_ptr = (subtype_t *)reshGetVal(s1_ID, &subtypeOps); if (subtype_ptr == NULL) Error("Internal error"); return subtypeCompareP(subtype_ptr,s2); } /* Print-out subtype data structure together with its attributes. Pointer version of this method. */ static void subtypePrintP(void * subtype_ptr, FILE * fp) { subtypePrintKernel((subtype_t *)subtype_ptr, fp); } /* Print-out subtype data structure together with its attributes. */ void subtypePrintPtr(subtype_t* subtype_ptr) { subtypePrintKernel(subtype_ptr, stdout); } /* Fill subtype data structure with default values. */ static void subtypeDefaultValue(subtype_t *subtype_ptr) { if (subtype_ptr == NULL) Error("Internal error!"); subtype_ptr->self = CDI_UNDEFID; subtype_ptr->nentries = 0; subtype_ptr->entries = NULL; subtype_ptr->globals.atts = NULL; subtype_ptr->globals.next = NULL; subtype_ptr->globals.self = -1; subtype_ptr->active_subtype_index = 0; } void subtypeAllocate(subtype_t **subtype_ptr2, int subtype) { /* allocate new subtype */ (*subtype_ptr2) = (subtype_t *) Malloc(sizeof(subtype_t)); subtype_t* subtype_ptr = *subtype_ptr2; subtypeDefaultValue(subtype_ptr); subtype_ptr->subtype = subtype; subtype_ptr->self = CDI_UNDEFID; } /* Create a copy of an existing subtype data structure. */ void subtypeDuplicate(subtype_t *subtype_ptr, subtype_t **dst_ptr) { if (subtype_ptr == NULL) Error("Internal error!"); subtypeAllocate(dst_ptr, subtype_ptr->subtype); subtype_t *dst = (*dst_ptr); /* create duplicate of subtype globals */ subtypeAttsDuplicate(subtype_ptr->globals.atts, &dst->globals); dst->globals.self = subtype_ptr->globals.self; /* create duplicate of subtype entries */ subtypeEntryDuplicate( subtype_ptr->entries, dst); } /* Register subtype object at resource handler. */ int subtypePush(subtype_t *subtype_ptr) { if (subtype_ptr == NULL) Error("Internal error!"); subtype_ptr->self = reshPut(subtype_ptr, &subtypeOps); return subtype_ptr->self; /* subtypeID */ } /* Sets an attribute for a subtype (for example a set of TILES). If the attribute has already been defined, then its value is overwritten. */ void subtypeDefGlobalDataP(subtype_t *subtype_ptr, int key, int val) { if (subtype_ptr == NULL) Error("Internal error!"); /* find entry in linked list or append otherwise */ struct subtype_attr_t* att_ptr = subtypeAttrFind(subtype_ptr->globals.atts, key); if (att_ptr == NULL) subtypeAttrInsert(&subtype_ptr->globals, key, val); else att_ptr->val = val; } /* Sets an attribute for a subtype (for example a set of TILES). If the attribute has already been defined, then its value is overwritten. */ void subtypeDefGlobalData(int subtypeID, int key, int val) { subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps); subtypeDefGlobalDataP(subtype_ptr, key, val); } /* Retrieves an attribute for a subtype (for example a set of TILES). If the attribute has not been defined, then return -1. */ int subtypeGetGlobalDataP(subtype_t *subtype_ptr, int key) { if (subtype_ptr == NULL) Error("Internal error!"); /* find entry in linked list */ struct subtype_attr_t* att_ptr = subtypeAttrFind(subtype_ptr->globals.atts, key); if (att_ptr == NULL) return -1; else return att_ptr->val; } /* Retrieves an attribute for a subtype (for example a set of TILES) . If the attribute has not been defined, then return -1. */ int subtypeGetGlobalData(int subtypeID, int key) { subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps); return subtypeGetGlobalDataP(subtype_ptr, key); } /* Sets an attribute for a single subtype entry (e.g. a single TILE). If the attribute has already been defined, then its value is overwritten. */ void subtypeDefEntryDataP(struct subtype_entry_t *subtype_entry_ptr, int key, int val) { if (subtype_entry_ptr == NULL) Error("Internal error!"); /* find entry in linked list or append otherwise */ struct subtype_attr_t* att_ptr = subtypeAttrFind(subtype_entry_ptr->atts, key); if (att_ptr == NULL) subtypeAttrInsert(subtype_entry_ptr, key, val); else att_ptr->val = val; } /* ------------------------------------------------------------------- */ /* IMPLEMENTATIONS FOR KEY-VALUE-PAIR QUERIES */ /* ------------------------------------------------------------------- */ /* Generate a "query object" out of a key-value pair. */ subtype_query_t keyValuePair(const char* key, int value) { subtype_query_t result; result.nAND = 1; result.key_value_pairs[0][0] = attribute_to_index(key); result.key_value_pairs[1][0] = value; if (CDI_Debug) { Message("key %s matches %d", key, result.key_value_pairs[0][0]); Message("%d --?-- %d", result.key_value_pairs[0][0], result.key_value_pairs[1][0]); } return result; } /* Generate an AND-combined "query object" out of two previous query objects. */ subtype_query_t matchAND(subtype_query_t q1, subtype_query_t q2) { if ((q1.nAND + q2.nAND) > MAX_KV_PAIRS_MATCH) Error("Internal error"); subtype_query_t result; result.nAND = q1.nAND; for (int i=0; ientries, *entry2 = s2->entries; struct subtype_attr_t *att_ptr2; /* test all entries of set 2 against set 1, to check if entry already exists: */ if (subtypeAttsCompare(s1->globals.atts, s2->globals.atts) != differ) { while (entry1 != NULL) { int found = 1; entry2 = s2->entries; while (entry2 != NULL) { found &= (subtypeAttsCompare(entry1->atts, entry2->atts) != differ); entry2 = entry2->next; } if (found) { return; } entry1 = entry1->next; } entry2 = s2->entries; while (entry2 != NULL) { entry1 = subtypeEntryInsert(s1); att_ptr2 = entry2->atts; while (att_ptr2 != NULL) { (void) subtypeAttrInsert(entry1, att_ptr2->key, att_ptr2->val); att_ptr2 = att_ptr2->next; } entry2 = entry2->next; } } else { fprintf(stderr, "\n# SUBTYPE A:\n"); subtypePrintKernel(s1, stderr); fprintf(stderr, "\n# SUBTYPE B:\n"); subtypePrintKernel(s2, stderr); Error("Attempting to insert subtype entry into subtype with different global attributes!"); } } /* ------------------------------------------------------------------- */ /* IMPLEMENTATIONS FOR ROUTINES VISIBLE THROUGH CDI.H */ /* ------------------------------------------------------------------- */ /* @Function subtypeCreate @Title Create a variable subtype @Prototype int subtypeCreate(int subtype) @Parameter @Item subtype The type of the variable subtype, one of the set of predefined CDI variable subtypes. The valid CDI variable subtypes are @func{SUBTYPE_TILES} @Description The function @func{subtypeCreate} creates a variable subtype. @Result @func{subtypeCreate} returns an identifier to the variable subtype. @EndFunction */ int subtypeCreate(int subtype) { if ( CDI_Debug ) Message("subtype: %d ", subtype); Message("subtype: %d ", subtype); /* allocate new subtype */ subtype_t *subtype_ptr; subtypeAllocate(&subtype_ptr, subtype); /* register object at resource handler */ return subtypePush(subtype_ptr); } /* Print-out subtype data structure together with its attributes. */ void subtypePrint(int subtypeID) { subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps); subtypePrintKernel(subtype_ptr, stdout); } /* Compares two subtype data structures. */ int subtypeCompare(int subtypeID1, int subtypeID2) { subtype_t *subtype_ptr1 = (subtype_t *)reshGetVal(subtypeID1, &subtypeOps); subtype_t *subtype_ptr2 = (subtype_t *)reshGetVal(subtypeID2, &subtypeOps); return subtypeCompareP(subtype_ptr1,subtype_ptr2); } /* Get the size of a subtype (e.g. no. of tiles). */ int subtypeInqSize(int subtypeID) { if ( subtypeID == CDI_UNDEFID ) { return 0; } else { subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps); return subtype_ptr->nentries; } } /* Get the currently active index of a subtype (e.g. current tile index). */ int subtypeInqActiveIndex(int subtypeID) { if (subtypeID == CDI_UNDEFID) return 0; subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps); return subtype_ptr->active_subtype_index; } /* Set the currently active index of a subtype (e.g. current tile index). */ void subtypeDefActiveIndex(int subtypeID, int index) { subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps); subtype_ptr->active_subtype_index = index; } /* subtypeInqSubEntry: Returns subtype entry ID for a given criterion. */ int subtypeInqSubEntry(int subtypeID, subtype_query_t criterion) { subtype_t *subtype_ptr = (subtype_t *)reshGetVal(subtypeID, &subtypeOps); struct subtype_entry_t *entry = subtype_ptr->entries; /* loop over all entries of this subtype */ while (entry != NULL) { { int match = 1; /* test if this entry matches ALL criteria. */ for (int j=0; (jatts, criterion.key_value_pairs[0][j]); if (att_ptr == NULL) { match = 0; if (CDI_Debug) Message("did not find %d", criterion.key_value_pairs[0][j]); } else { if (CDI_Debug) Message("found %d", criterion.key_value_pairs[0][j]); match &= (att_ptr->val == criterion.key_value_pairs[1][j]); } } if (match) return entry->self; } entry = entry->next; } return CDI_UNDEFID; } int subtypeInqTile(int subtypeID, int tileindex, int attribute) { return subtypeInqSubEntry(subtypeID, matchAND(keyValuePair(cdiSubtypeAttributeName[SUBTYPE_ATT_TILEINDEX], tileindex), keyValuePair(cdiSubtypeAttributeName[SUBTYPE_ATT_TILEATTRIBUTE], attribute))); } int subtypeInqAttribute(int subtypeID, int index, const char* key, int* outValue) { //Validate input params. if(subtypeID == CDI_UNDEFID) xabort("CDI_UNDEFID was passed to %s() as a subtypeID. Please check the origin of that ID.", __func__); subtype_t *subtype_ptr = reshGetVal(subtypeID, &subtypeOps); if(!subtype_ptr) xabort("Internal error: subtypeID %d resolved to NULL.", subtypeID); if((unsigned)index >= (unsigned)subtype_ptr->nentries) { xabort("index argument of %s() is out of range. Expected 0 <= index < %d, but got index = %d.", __func__, subtype_ptr->nentries, index); } if(!outValue) outValue = &(int){0}; if(!key) return CDI_EINVAL; int iKey = attribute_to_index(key); if(iKey < 0) return CDI_EINVAL; //Find the entry. struct subtype_entry_t* entry = subtype_ptr->entries; for(; index--; entry = entry->next) if(!entry) xabort("internal error: preliminary end of subtype entry list"); //Find the attribute. for(struct subtype_attr_t* attribute = entry->atts; attribute; attribute = attribute->next) { if(attribute->key == iKey) { *outValue = attribute->val; return CDI_NOERR; } } //Failed to find the attribute if this point is reached. return CDI_EINVAL; } /* Construct a new subtype for a tile set. If a corresponding subtype * already exists, then we return this subtype ID instead. * * See comment on subtype.c::tilesetMatchingPtr for the specification * of the term "corresponding" tile set. */ int vlistDefTileSubtype(int vlistID, subtype_t *tiles) { int subtypeID = CDI_UNDEFID; /* loop over subtypes and search for an identical tileset */ vlist_t *vlistptr = vlist_to_pointer(vlistID); int tileset_defined = 0; for (int isub=0; isubnsubtypes; isub++) { /* get the ID of the "isub"th subtype */ subtypeID = vlistptr->subtypeIDs[isub]; if (subtypeComparePtr(subtypeID, tiles) == 0) { tileset_defined = 1; break; } } /* tile set seems to be new: register at resource handler. */ if (tileset_defined == 0) { subtype_t *tiles_duplicate = NULL; subtypeDuplicate(tiles, &tiles_duplicate); subtypeID = vlistptr->subtypeIDs[vlistptr->nsubtypes++] = subtypePush(tiles_duplicate); } return subtypeID; } int vlistInsertTrivialTileSubtype(int vlistID) { /* first, generate a subtype */ subtype_t *subtype_ptr; subtypeAllocate(&subtype_ptr, SUBTYPE_TILES); /* create a tile set that contains only one tile/attribute pair. */ (void) subtypeEntryInsert(subtype_ptr); /* register tile */ vlist_t *vlistptr = vlist_to_pointer(vlistID); int subtypeID = vlistptr->subtypeIDs[vlistptr->nsubtypes++] = subtypePush(subtype_ptr); return subtypeID; } /* ------------------------------------------------------------------- */ /* NOT YET IMPLEMENTED */ /* ------------------------------------------------------------------- */ static int subtypeGetPackSize( void * subtype_ptr, void *context) { (void)subtype_ptr; (void)context; Error("Not yet implemented for subtypes!"); return 0; } static void subtypePack( void * subtype_ptr, void * buffer, int size, int *pos, void *context) { (void)subtype_ptr; (void)buffer; (void)size; (void)pos; (void)context; Error("Not yet implemented for subtypes!"); } static int subtypeTxCode( void ) { Error("Not yet implemented for subtypes!"); return 0; } /* * Local Variables: * c-file-style: "Java" * c-basic-offset: 2 * indent-tabs-mode: nil * show-trailing-whitespace: t * require-trailing-newline: t * End: */