pjs/lib/libstyle/stystruc.c

765 строки
15 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.
*/
/* 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 */