/* -*- 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. */ /* style struck used to hold style associations * * Designed and Implemented by Lou Montulli '97 */ #include "xp.h" #include "stystruc.h" /* class to hold style information between style sheet parser * and layout engine */ #define INCREASE_ARRAY_BY 5 #define INITIAL_ARRAY_SIZE 10 typedef enum { CHAR_VALUE, SS_NUM_VALUE } ss_pair_type; typedef struct _ss_pair { char *name; ss_pair_type value_type; void *value; int32 priority; } ss_pair; typedef struct _SS_StyleStruct { StyleStructInterface *vtable; int32 refcount; /* private data */ ss_pair ** pair_array; int32 pair_array_size; int32 pair_array_first_unused_index; } SS_StyleStruct; /* * allocate SS_StyleStructs from a pool */ static XP_AllocStructInfo SS_StyleStructAlloc = { XP_INITIALIZE_ALLOCSTRUCTINFO(sizeof(SS_StyleStruct)) }; void SS_freeSSNumber(SS_StyleStruct *self, SS_Number *obj) { if(!obj) return; XP_FREEIF(obj->units); XP_FREE(obj); } SS_Number * SS_newSSNumber(SS_StyleStruct *self, double value, char *units) { SS_Number *new_num; if(!units) { XP_ASSERT(0); return NULL; } /* alloc an ss_num */ new_num = XP_CALLOC(1, sizeof(SS_Number)); if(!new_num) return NULL; new_num->value = value; new_num->units = XP_STRDUP(units); if(!new_num->units) { XP_FREE(new_num); return NULL; } return(new_num); } SS_Number * SS_copySSNumber(SS_StyleStruct *self, SS_Number *old_num) { if(!old_num) { XP_ASSERT(0); return NULL; } return(SS_newSSNumber(self, old_num->value, old_num->units)); } void ss_expand_array(SS_StyleStruct *self) { if(self->pair_array) { self->pair_array_size += INCREASE_ARRAY_BY; self->pair_array = XP_REALLOC(self->pair_array, self->pair_array_size*sizeof(ss_pair*)); } else { self->pair_array_size = INITIAL_ARRAY_SIZE; self->pair_array = XP_CALLOC(self->pair_array_size, sizeof(ss_pair*)); } if(!self->pair_array) { self->pair_array_first_unused_index = 0; self->pair_array_size = 0; } } void ss_free_ss_pair(SS_StyleStruct *self, ss_pair *pair) { if(pair) { XP_FREEIF(pair->name); switch(pair->value_type) { case CHAR_VALUE: XP_FREEIF(pair->value); break; case SS_NUM_VALUE: SS_freeSSNumber(self, (SS_Number*)pair->value); break; default: XP_ASSERT(0); } XP_FREE(pair); } } ss_pair * ss_find_pair(SS_StyleStruct *self, char *name) { int i; for(i = 0; i < self->pair_array_first_unused_index; i++) { if(!strcasecomp(self->pair_array[i]->name, name)) return(self->pair_array[i]); } return NULL; } int32 ss_find_pair_index(SS_StyleStruct *self, ss_pair *pair) { int i; for(i = 0; i < self->pair_array_first_unused_index; i++) { if((self->pair_array[i] == pair)) return(i); } return -1; } /* remove the pair from the pair array and * free's the pair if found. */ void ss_delete_pair(SS_StyleStruct *self, ss_pair *pair) { /* find the pair index */ int32 index = ss_find_pair_index(self, pair); if(index > -1) { ss_free_ss_pair(self, self->pair_array[index]); /* shift the array back over the deleted pair */ if(index < self->pair_array_first_unused_index) { int32 move_size = (self->pair_array_first_unused_index-index)-1; move_size *= sizeof(ss_pair*); if(move_size) XP_MEMMOVE(&self->pair_array[index], &self->pair_array[index+1], move_size); self->pair_array_first_unused_index--; } } else { /* cant find pair */ XP_ASSERT(0); } } void ss_add_to_array(SS_StyleStruct *self, ss_pair *pair) { if(!pair || !pair->name) { XP_ASSERT(0); return; } if(self->pair_array_size <= self->pair_array_first_unused_index) ss_expand_array(self); if(self->pair_array) { #if DEBUG /* check for dups */ ss_pair *dup = ss_find_pair(self, pair->name); if(dup) XP_ASSERT(0); #endif self->pair_array[self->pair_array_first_unused_index++] = pair; } else { ss_free_ss_pair(self, pair); } } void ss_free_pair_array(SS_StyleStruct *self) { int i; if(self->pair_array) { for(i = 0; i < self->pair_array_first_unused_index; i++) { ss_free_ss_pair(self, self->pair_array[i]); } XP_FREE(self->pair_array); } self->pair_array_size = 0; self->pair_array_first_unused_index = 0; } void SS_setString(SS_StyleStruct *self, char *name, char *value, int32 priority) { ss_pair *new_pair; ss_pair *dup; /* check for dups */ dup = ss_find_pair(self, name); if(dup) { if(dup->priority <= priority) ss_delete_pair(self, dup); else return; /* ignore this set call */ } /* alloc a pair */ new_pair = XP_CALLOC(1, sizeof(ss_pair)); if(new_pair) { new_pair->name = XP_STRDUP(name); new_pair->value_type = CHAR_VALUE; new_pair->value = XP_STRDUP(value); new_pair->priority = priority; if(!new_pair->name || !new_pair->value) { /* malloc error */ XP_FREE(new_pair); XP_FREEIF(new_pair->name); XP_FREEIF(new_pair->value); return; } ss_add_to_array(self, new_pair); } } void SS_setNumber(SS_StyleStruct *self, char *name, SS_Number *value, int32 priority) { SS_Number *ss_num; ss_pair *new_pair; ss_pair *dup; /* check for dups */ dup = ss_find_pair(self, name); if(dup) { if(dup->priority <= priority) ss_delete_pair(self, dup); else return; /* ignore this set call */ } /* alloc a pair */ new_pair = XP_CALLOC(1, sizeof(ss_pair)); if(new_pair) { /* alloc an ss_num */ ss_num = SS_copySSNumber(self, value); new_pair->name = XP_STRDUP(name); new_pair->value_type = SS_NUM_VALUE; new_pair->value = ss_num; new_pair->priority = priority; if(!new_pair->name || !new_pair->value) { /* malloc error */ XP_FREE(new_pair); XP_FREEIF(new_pair->name); SS_freeSSNumber(self, new_pair->value); return; } ss_add_to_array(self, new_pair); } } char * SS_getString(SS_StyleStruct *self, char *name) { ss_pair *pair = ss_find_pair(self, name); if(pair && pair->value) { if(pair->value_type == CHAR_VALUE) { return(XP_STRDUP((char*)pair->value)); } else if(pair->value_type == SS_NUM_VALUE) { SS_Number *ss_num = (SS_Number*)pair->value; char *rv = PR_smprintf("%f", ss_num->value); StrAllocCat(rv, ss_num->units); return(rv); } else { XP_ASSERT(0); } } return(NULL); } SS_Number * SS_stringToSSNumber(SS_StyleStruct *self, char *num_string) { char *ptr, *num_ptr; double num_val; ptr = num_string; /* skip any whitespace */ while(XP_IS_SPACE(*ptr)) ptr++; /* save a pointer to the first non white char */ num_ptr = ptr; /* go past any sign in front of the number */ if(*ptr == '-' || *ptr == '+') ptr++; /* go forward until a non number is encountered */ while(XP_IS_DIGIT(*ptr)) ptr++; /* go past a decimal */ if(*ptr == '.') ptr++; while(XP_IS_DIGIT(*ptr)) ptr++; /* skip any whitespace between the number and units */ while(XP_IS_SPACE(*ptr)) ptr++; /* * no need to clear out the string at the end since * atof will do that for us, and writting to the string * will make us crash * * ptr_value = *ptr; * *ptr = '\0'; * *ptr = ptr_value; */ num_val = atof(num_ptr); return(SS_newSSNumber(self, num_val, ptr)); } SS_Number * SS_getNumber(SS_StyleStruct *self, char *name) { ss_pair *pair = ss_find_pair(self, name); if(pair && pair->value) { if(pair->value_type == CHAR_VALUE) { return SS_stringToSSNumber(self, pair->value); } else if(pair->value_type == SS_NUM_VALUE) { return(SS_copySSNumber(self, (SS_Number*)pair->value)); } else { XP_ASSERT(0); } } return(NULL); } uint32 SS_count(SS_StyleStruct *self) { return self->pair_array_first_unused_index; } StyleStruct * SS_duplicate(SS_StyleStruct *self) { StyleStruct *new_ss = STYLESTRUCT_Factory_Create(); int i; if(!new_ss) return NULL; /* add all the elements of the current struct to the new one */ for(i = 0; i < self->pair_array_first_unused_index; i++) { ss_pair *pair = self->pair_array[i]; switch(pair->value_type) { case CHAR_VALUE: STYLESTRUCT_SetString(new_ss, pair->name, (char *)pair->value, pair->priority); break; case SS_NUM_VALUE: STYLESTRUCT_SetNumber(new_ss, pair->name, (SS_Number*)pair->value, pair->priority); break; default: XP_ASSERT(0); } } return new_ss; } void SS_delete(SS_StyleStruct *self) { self->refcount--; if(self->refcount > 0) return; ss_free_pair_array(self); #ifdef DEBUG /* memset the struct so that any free memory read * errors are immediately detectable */ XP_MEMSET(self, 0, sizeof(SS_StyleStruct)); #endif /* DEBUG */ /* *return SS_StyleStruct to the pool */ XP_FreeStruct(&SS_StyleStructAlloc, self); self = NULL; } /***************************************************** * class symantics */ /* static vtable */ const StyleStructInterface StyleStruct_interface = { (SS_Number* (*)(StyleStruct *self, double value, char *units)) SS_newSSNumber, (void (*)(StyleStruct * self, SS_Number *obj)) SS_freeSSNumber, (SS_Number* (*)(StyleStruct *self, SS_Number *old_num)) SS_copySSNumber, (SS_Number* (*)(StyleStruct *self, char *strng)) SS_stringToSSNumber, (void (*)(StyleStruct * self, char *name, char *value, int32 priority)) SS_setString, (void (*)(StyleStruct * self, char *name, SS_Number *value, int32 priority)) SS_setNumber, (char* (*)(StyleStruct * self, char *name)) SS_getString, (SS_Number* (*)(StyleStruct * self, char *name)) SS_getNumber, (uint32 (*)(StyleStruct * self)) SS_count, (StyleStruct * (*)(StyleStruct * self)) SS_duplicate, (void (*)(StyleStruct * self)) SS_delete, }; StyleStruct * STYLESTRUCT_Factory_Create(void) { /* initializer */ /* * allocate SS_StyleStruct from a pool */ SS_StyleStruct *self = (SS_StyleStruct*) XP_AllocStructZero(&SS_StyleStructAlloc); if(!self) return NULL; self->vtable = (void*)&StyleStruct_interface; self->refcount = 1; return (StyleStruct*)self; } #ifdef SS_TEST typedef struct { char name[100]; char value[100]; char units[100]; ss_pair_type type; } test_struct; test_struct test_table[] = { {"numone", "1", "pts", SS_NUM_VALUE}, {"numtwo", "2", "pts", SS_NUM_VALUE}, {"numthree", "3", "pts", SS_NUM_VALUE}, {"numfour", "4", "pts", SS_NUM_VALUE}, {"numfive", "5", "pts", SS_NUM_VALUE}, {"numsix", "6", "pts", SS_NUM_VALUE}, {"numseven", "7", "pts", SS_NUM_VALUE}, {"numeight", "8", "pts", SS_NUM_VALUE}, {"numnine", "9", "pts", SS_NUM_VALUE}, {"numten", "10", "pts", SS_NUM_VALUE}, {"numeleven", "11", "pts", SS_NUM_VALUE}, {"numtwelve", "12", "pts", SS_NUM_VALUE}, {"numthirteen plus spaces", "13", "pts", SS_NUM_VALUE}, {"numfive hundred thowsand", "500000", "pts", SS_NUM_VALUE}, {"strone", "strone", "", CHAR_VALUE}, {"strtwo", "strtwo", "", CHAR_VALUE}, {"strthree", "strthree", "", CHAR_VALUE}, {"strfour", "strfour", "", CHAR_VALUE}, {"strfive", "strfive", "", CHAR_VALUE}, {"strsix", "strsix", "", CHAR_VALUE}, {"strseven", "strseven", "", CHAR_VALUE}, {0, 0, SS_NUM_VALUE} }; void test_values(StyleStruct *h) { int i; char *ptr; SS_Number *ss; char buf[200]; for(i=0; *test_table[i].name; i++) { test_struct *ts = &test_table[i]; printf("testing name: %s\n", ts->name); switch(ts->type) { case CHAR_VALUE: ptr = STYLESTRUCT_GetString(h, ts->name); if(!ptr) { printf("Error: value not found for name: %s\n", ts->name); } else if(strcmp(ptr, ts->value)) { printf("Error: value does not match, old: %s new: %s\n", ts->value, ptr); XP_FREE(ptr); } break; case SS_NUM_VALUE: /* get as string */ ptr = STYLESTRUCT_GetString(h, ts->name); if(!ptr) { printf("Error: value not found for name: %s\n", ts->name); } else { strcpy(buf, ts->value); strcat(buf, ts->units); if(strcmp(ptr, buf)) printf("Error: value does not match, old: %s new: %s\n", buf, ptr); XP_FREE(ptr); } /* get as number */ ss = STYLESTRUCT_GetNumber(h, ts->name); if(!ss) { printf("Error: value not found for name: %s\n", ts->name); } else { if(ss->value != atof(ts->value)) printf("Error: value does not match, old: %s new: %d\n", ts->value, ss->value); if(strcmp(ss->units, ts->units)) printf("Error: value does not match, old: %s new: %s\n", ts->units, ss->units); STYLESTRUCT_FreeSSNumber(h, ss); } break; default: XP_ASSERT(0); break; } } /* test for some names that dont exist */ ptr = STYLESTRUCT_GetString(h, "DOESN'T EXIST"); if(ptr) { printf("Error: returned value should not have been found"); XP_FREE(ptr); } ss = STYLESTRUCT_GetNumber(h, "THIS NAME DOES NOT EXIST"); if(ss) { printf("Error: returned value should not have been found"); STYLESTRUCT_FreeSSNumber(h, ss); } } int main(int argc, char *argv[]) { int i; StyleStruct *h; char buf[200]; SS_Number *ss_num; StyleStruct *new_ss; h = STYLESTRUCT_Factory_Create(); if(!h) exit(1); /* add everything as strings */ for(i=0; *test_table[i].name; i++) { test_struct *ts = &test_table[i]; switch(ts->type) { case CHAR_VALUE: STYLESTRUCT_SetString(h, ts->name, ts->value); break; case SS_NUM_VALUE: strcpy(buf, ts->value); strcat(buf, ts->units); STYLESTRUCT_SetString(h, ts->name, buf); break; default: XP_ASSERT(0); break; } } test_values(h); /* add strings and numbers */ for(i=0; *test_table[i].name; i++) { test_struct *ts = &test_table[i]; switch(ts->type) { case CHAR_VALUE: STYLESTRUCT_SetString(h, ts->name, ts->value); break; case SS_NUM_VALUE: ss_num = STYLESTRUCT_NewSSNumber(h, atol(ts->value), ts->units); STYLESTRUCT_SetNumber(h, ts->name, ss_num, 0); STYLESTRUCT_FreeSSNumber(h, ss_num); break; default: XP_ASSERT(0); break; } } test_values(h); /* dup the class */ new_ss = STYLESTRUCT_Duplicate(h); STYLESTRUCT_Delete(h); test_values(new_ss); STYLESTRUCT_Delete(new_ss); printf("all tests passed\n\n"); } #endif /* TEST_SS */