/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (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.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ /* styleStack used to hold a stack of tags and associated styles * * Designed and Implemented by Lou Montulli '97 */ #include "xp.h" #include "libmocha.h" #include "stystruc.h" #include "stystack.h" #include "jsspriv.h" /* simple stack implementation for style/tag stack * why don't we have an XP Stack? */ #define INITIAL_STACK_SIZE 10 #define INCREASE_STACK_BY 5 typedef struct { StyleStruct *style; TagStruct *tag; } TagAndStyleAssoc; typedef struct _SML_StyleAndTagStack { StyleAndTagStackInterface *vtable; int32 refcount; /* private data */ XP_Bool save_stack; TagAndStyleAssoc **tag_stack; int32 tag_stack_size; int32 tag_stack_first_unused_index; MWContext *context; StyleObject *tags; StyleObject *classes; StyleObject *ids; } SML_StyleAndTagStack; /* specify whether to keep the stack or not. * when the stack is OFF (as it is at initalization) * adding to the stack is a noop */ void SML_SetSaveOn(SML_StyleAndTagStack *self, XP_Bool on_flag) { self->save_stack = on_flag; } XP_Bool SML_IsSaveOn(SML_StyleAndTagStack *self) { return self->save_stack; } /* * allocate TagStrucs from a pool to speed up * the application */ static XP_AllocStructInfo TagStructAlloc = { XP_INITIALIZE_ALLOCSTRUCTINFO(sizeof(TagStruct)) }; TagStruct * SML_NewTagStruct(SML_StyleAndTagStack *self, char *name, char *class_name, char *id) { TagStruct *tag; if(!name) return NULL; /* * allocate TagStrucs from a pool to speed up * the application */ tag = (TagStruct*) XP_AllocStructZero(&TagStructAlloc); if(!tag) return(NULL); tag->name = name; if(class_name) tag->class_name = class_name; if(id) tag->id = id; return(tag); } void SML_FreeTagStruct(SML_StyleAndTagStack *self, TagStruct *tag) { if(!tag) return; XP_FREEIF(tag->name); XP_FREEIF(tag->class_name); XP_FREEIF(tag->id); /* * return the TagStruct to the pool */ XP_FreeStruct(&TagStructAlloc, tag); tag = NULL; } /* * allocate TagAndStyleAssoc from a pool to speed up * the application */ static XP_AllocStructInfo TagAndStyleAssocAlloc = { XP_INITIALIZE_ALLOCSTRUCTINFO(sizeof(TagAndStyleAssoc)) }; TagAndStyleAssoc * sml_new_assoc(SML_StyleAndTagStack *self, TagStruct *tag, StyleStruct *style) { TagAndStyleAssoc *new_assoc; if(!tag || !style) { XP_ASSERT(0); return NULL; } /* * allocate TagAndStyleAssoc from a pool to speed up * the application */ new_assoc = (TagAndStyleAssoc*) XP_AllocStructZero(&TagAndStyleAssocAlloc); if(!new_assoc) return NULL; new_assoc->tag = tag; new_assoc->style = style; return new_assoc; } void sml_free_TagAndStyleAssoc(SML_StyleAndTagStack *self,TagAndStyleAssoc *assoc) { if(!assoc) return; SML_FreeTagStruct(self, assoc->tag); STYLESTRUCT_Delete(assoc->style); #ifdef DEBUG assoc->tag = (TagStruct *)-1; assoc->style = (StyleStruct *)-1; #endif /* * return the TagAndStyleAssoc to the pool */ XP_FreeStruct(&TagAndStyleAssocAlloc, assoc); assoc = NULL; } void sml_free_stack(SML_StyleAndTagStack *self) { int i; if(self->tag_stack) { for(i = 0; i < self->tag_stack_first_unused_index; i++) { sml_free_TagAndStyleAssoc(self, self->tag_stack[i]); } XP_FREE(self->tag_stack); } self->tag_stack_size = 0; self->tag_stack_first_unused_index = 0; self->tag_stack = NULL; } void sml_expand_stack(SML_StyleAndTagStack *self) { if(self->tag_stack) { self->tag_stack_size += INCREASE_STACK_BY; self->tag_stack = XP_REALLOC(self->tag_stack, self->tag_stack_size*sizeof(TagAndStyleAssoc*)); } else { self->tag_stack_size = INITIAL_STACK_SIZE; self->tag_stack = XP_CALLOC(self->tag_stack_size, sizeof(TagAndStyleAssoc*)); } if(!self->tag_stack) { self->tag_stack_first_unused_index = 0; self->tag_stack_size = 0; } } void ss_add_to_stack(SML_StyleAndTagStack *self, TagAndStyleAssoc *pair) { if(self->tag_stack_size <= self->tag_stack_first_unused_index) sml_expand_stack(self); if(self->tag_stack) { self->tag_stack[self->tag_stack_first_unused_index++] = pair; } else { sml_free_TagAndStyleAssoc(self, pair); } } PushTagStatus SML_PushTag(SML_StyleAndTagStack *self, char *name, char *class_name, char *id) { TagStruct *new_tag; StyleStruct *ss; TagAndStyleAssoc *assoc; if(!self->save_stack) return PUSH_TAG_ERROR; new_tag = SML_NewTagStruct(self, name, class_name, id); if(!new_tag) return PUSH_TAG_ERROR; ss = STYLESTRUCT_Factory_Create(); if(!ss) { SML_FreeTagStruct(self, new_tag); return PUSH_TAG_ERROR; } assoc = sml_new_assoc(self, new_tag, ss); if(!assoc) { SML_FreeTagStruct(self, new_tag); STYLESTRUCT_Delete(ss); return PUSH_TAG_ERROR; } ss_add_to_stack(self, assoc); /* add call to style sheet parser here to fill in style struct */ jss_GetStyleForTopTag((StyleAndTagStack*)self); return(PUSH_TAG_SUCCESS); } /* pop a tag from within the stack * The stack is indexed from the top element. * the zero'th element is the top of the stack */ void SML_PopTagByIndex(SML_StyleAndTagStack *self, char *name, int32 index) { TagAndStyleAssoc *assoc; /* index from the top of the stack */ int32 real_index = self->tag_stack_first_unused_index - 1 - index; if(self->tag_stack_first_unused_index < real_index+1 || real_index < 0) return; assoc = self->tag_stack[real_index]; sml_free_TagAndStyleAssoc(self, assoc); /* shift the stack down */ if(real_index < self->tag_stack_first_unused_index-1) XP_MEMMOVE(&self->tag_stack[real_index], &self->tag_stack[real_index+1], index*sizeof(TagAndStyleAssoc*)); /* zero last unused index for debugging purposes */ self->tag_stack_first_unused_index--; self->tag_stack[self->tag_stack_first_unused_index] = 0; } void SML_PopTag(SML_StyleAndTagStack *self, char *name) { SML_PopTagByIndex(self, name, 0); } /* the zero'th index is the top of the stack */ TagAndStyleAssoc * sml_get_index(SML_StyleAndTagStack *self, int32 index) { int32 real_index = (self->tag_stack_first_unused_index-1) - index; if(real_index < 0) return NULL; return(self->tag_stack[real_index]); } /* the zero'th index is the bottom of the stack */ TagAndStyleAssoc * sml_get_reverse_index(SML_StyleAndTagStack *self, int32 index) { if(index >= self->tag_stack_first_unused_index) return NULL; return(self->tag_stack[index]); } /* the zero'th index is the top of the stack */ TagStruct * SML_GetTagByIndex(SML_StyleAndTagStack *self, int32 index) { TagAndStyleAssoc *assoc = sml_get_index(self, index); if(!assoc) return NULL; return(assoc->tag); } /* the zero'th index is the bottom of the stack */ TagStruct * SML_GetTagByReverseIndex(SML_StyleAndTagStack *self, int32 index) { TagAndStyleAssoc *assoc = sml_get_reverse_index(self, index); if(!assoc) return NULL; return(assoc->tag); } /* the zero'th index is the top of the stack */ StyleStruct * SML_GetStyleByIndex(SML_StyleAndTagStack *self, int32 index) { TagAndStyleAssoc *assoc = sml_get_index(self, index); if(!assoc) return NULL; return(assoc->style); } /* the zero'th index is the bottom of the stack */ StyleStruct * SML_GetStyleByReverseIndex(SML_StyleAndTagStack *self, int32 index) { TagAndStyleAssoc *assoc = sml_get_reverse_index(self, index); if(!assoc) return NULL; return(assoc->style); } void SML_Delete(SML_StyleAndTagStack *self) { self->refcount--; if(self->refcount > 0) return; sml_free_stack(self); XP_FREE(self); } void SML_Purge(SML_StyleAndTagStack *self) { sml_free_stack(self); } void SML_Init(SML_StyleAndTagStack *self, MWContext *context) { self->context = context; } /* static vtable */ const StyleAndTagStackInterface StyleAndTagStack_interface = { (void (*)(StyleAndTagStack *self, MWContext *context)) SML_Init, (TagStruct * (*)(StyleAndTagStack *self, char *name, char *class_name, char *id)) SML_NewTagStruct, (void (*)(StyleAndTagStack *self, TagStruct *tag)) SML_FreeTagStruct, (PushTagStatus (*)(StyleAndTagStack *self, char *name, char *class_name, char *id)) SML_PushTag, (void (*)(StyleAndTagStack *self, char *name)) SML_PopTag, (void (*)(StyleAndTagStack *self, char *name, int32 index)) SML_PopTagByIndex, (TagStruct * (*)(StyleAndTagStack *self, int32 index)) SML_GetTagByIndex, (TagStruct * (*)(StyleAndTagStack *self, int32 index)) SML_GetTagByReverseIndex, (StyleStruct * (*)(StyleAndTagStack *self, int32 index)) SML_GetStyleByIndex, (StyleStruct * (*)(StyleAndTagStack *self, int32 index)) SML_GetStyleByReverseIndex, (StyleStruct * (*)(StyleAndTagStack *self)) SML_Delete, (StyleStruct * (*)(StyleAndTagStack *self)) SML_Purge, (void (*)(StyleAndTagStack *self, XP_Bool on_flag)) SML_SetSaveOn, (XP_Bool (*)(StyleAndTagStack *self)) SML_IsSaveOn }; StyleAndTagStack * SML_StyleStack_Factory_Create(void) { /* initializer */ SML_StyleAndTagStack * self = (SML_StyleAndTagStack*)XP_CALLOC(1, sizeof(SML_StyleAndTagStack)); if(!self) return NULL; self->vtable = (void*)&StyleAndTagStack_interface; self->refcount = 1; return (StyleAndTagStack*)self; } void SML_SetObjectRefs(StyleAndTagStack *styleStack, StyleObject *tags, StyleObject *classes, StyleObject *ids) { SML_StyleAndTagStack * self = (SML_StyleAndTagStack*)styleStack; self->tags = tags; self->classes = classes; self->ids = ids; } /* * Helper routine to assemble the JSSContext */ void sml_GetJSSContext(StyleAndTagStack *styleStack, JSSContext *jc) { SML_StyleAndTagStack * self = (SML_StyleAndTagStack*)styleStack; /* Lookup document.tags */ jc->tags = self->tags; /* Lookup document.classes */ jc->classes = self->classes; /* Lookup up document.ids */ jc->ids = self->ids; } #ifdef TEST_STYLESTACK typedef struct { char name[100]; char class_name[100]; char id[100]; } test_struct; test_struct test_table[] = { {"h1", "class1", "id1"}, {"h2", "class2", "id2"}, {"h3", "class3", "id3"}, {"h4", "class4", "id4"}, {"h5", "", "id1"}, {"h6", "", "id2"}, {"h7", "", "id3"}, {"h8", "", "id4"}, {"h9", "class9", ""}, {"h10", "class10", ""}, {"h11", "class11", ""}, {"h12", "class12", ""}, {"h13", "", ""}, {"h14", "", ""}, {"h15", "", ""}, {"h16", "", ""}, {"", "", ""} }; void test_values(StyleAndTagStack * h) { int i; for(i=0; *test_table[i].name; i++) ; /* null body */ for(i--; i >= 0; i--) { test_struct *ts = &test_table[i]; TagStruct *tag = STYLESTACK_GetTagByIndex(h, 0); if(strcmp(tag->name, ts->name)) printf("Error: names do not match: %s:%s\n", tag->name, ts->name); else if(*ts->class && strcmp(tag->class, ts->class)) printf("Error: class names do not match: %s:%s\n", tag->class_name, ts->class_name); else if(*ts->id && strcmp(tag->id, ts->id)) printf("Error: id names do not match: %s:%s\n", tag->id, ts->id); else printf("Success: retreival successful: %s\n", tag->name); STYLESTACK_PopTag(h, ts->name); } } int main(int argc, char *argv[]) { int i; StyleAndTagStack * h; StyleStruct *style; h = SML_StyleStack_Factory_Create(); if(!h) exit(1); for(i=0; *test_table[i].name; i++) { test_struct *ts = &test_table[i]; style = STYLESTACK_PushTag(h, ts->name, ts->class_name, ts->id); if(!style) printf("stack (or malloc) error"); } test_values(h); printf("\nAll tests complete\n"); exit(0); } #endif /* TEST_STYLESTACK */