pjs/lib/libstyle/stystack.c

587 строки
12 KiB
C

/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/* 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 */