/* -*- Mode: C -*- */
/*======================================================================
FILE: icalgauge.c
CREATOR: eric 23 December 1999
$Id$
$Locker$
(C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org
This program is free software; you can redistribute it and/or modify
it under the terms of either:
The LGPL as published by the Free Software Foundation, version
2.1, available at: http://www.fsf.org/copyleft/lesser.html
Or:
The Mozilla Public License Version 1.0. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
The Original Code is eric. The Initial Developer of the Original
Code is Eric Busboom
======================================================================*/
#include "ical.h"
#include "icalgauge.h"
#include "icalgaugeimpl.h"
#include <stdlib.h>
extern char* input_buffer;
extern char* input_buffer_p;
int ssparse(void);
struct icalgauge_impl *icalss_yy_gauge;
icalgauge* icalgauge_new_from_sql(char* sql)
{
struct icalgauge_impl *impl;
int r;
if ( ( impl = (struct icalgauge_impl*)
malloc(sizeof(struct icalgauge_impl))) == 0) {
icalerror_set_errno(ICAL_NEWFAILED_ERROR);
return 0;
}
impl->select = pvl_newlist();
impl->from = pvl_newlist();
impl->where = pvl_newlist();
icalss_yy_gauge = impl;
input_buffer_p = input_buffer = sql;
r = ssparse();
return impl;
}
void icalgauge_free(icalgauge* gauge)
{
struct icalgauge_impl *impl = (struct icalgauge_impl*)gauge;
struct icalgauge_where *w;
assert(impl->select != 0);
assert(impl->where != 0);
assert(impl->from != 0);
if(impl->select){
while( (w=pvl_pop(impl->select)) != 0){
if(w->value != 0){
free(w->value);
}
free(w);
}
pvl_free(impl->select);
}
if(impl->where){
while( (w=pvl_pop(impl->where)) != 0){
if(w->value != 0){
free(w->value);
}
free(w);
}
pvl_free(impl->where);
}
if(impl->from){
pvl_free(impl->from);
}
}
/* Convert a VQUERY component into a gauge */
icalcomponent* icalgauge_make_gauge(icalcomponent* query);
/* icaldirset_test compares a component against a gauge, and returns
true if the component passes the test
The gauge is a VCALENDAR component that specifies how to test the
target components. The guage holds a collection of VEVENT, VTODO or
VJOURNAL sub-components. Each of the sub-components has a
collection of properties that are compared to corresponding
properties in the target component, according to the
X-LIC-COMPARETYPE parameters to the gauge's properties.
When a gauge has several sub-components, the results of testing the
target against each of them is ORed together - the target
component will pass if it matches any of the sub-components in the
gauge. However, the results of matching the properties in a
sub-component are ANDed -- the target must match every property in
a gauge sub-component to match the sub-component.
Here is an example:
BEGIN:XROOT
DTSTART;X-LIC-COMPARETYPE=LESS:19981025T020000
ORGANIZER;X-LIC-COMPARETYPE=EQUAL:mrbig@host.com
END:XROOT
BEGIN:XROOT
LOCATION;X-LIC-COMPARETYPE=EQUAL:McNary's Pub
END:XROOT
This gauge has two sub-components; one which will match a VEVENT
based on start time, and organizer, and another that matches based
on LOCATION. A target component will pass the test if it matched
either of the sub-components.
*/
int icalgauge_compare_recurse(icalcomponent* comp, icalcomponent* gauge)
{
int pass = 1,localpass = 0;
icalproperty *p;
icalcomponent *child,*subgauge;
icalcomponent_kind gaugekind, compkind;
icalerror_check_arg_rz( (comp!=0), "comp");
icalerror_check_arg_rz( (gauge!=0), "gauge");
gaugekind = icalcomponent_isa(gauge);
compkind = icalcomponent_isa(comp);
if( ! (gaugekind == compkind || gaugekind == ICAL_ANY_COMPONENT) ){
return 0;
}
/* Test properties. For each property in the gauge, search through
the component for a similar property. If one is found, compare
the two properties value with the comparison specified in the
gauge with the X-LIC-COMPARETYPE parameter */
for(p = icalcomponent_get_first_property(gauge,ICAL_ANY_PROPERTY);
p != 0;
p = icalcomponent_get_next_property(gauge,ICAL_ANY_PROPERTY)){
icalproperty* targetprop;
icalparameter* compareparam;
icalparameter_xliccomparetype compare;
int rel; /* The relationship between the gauge and target values.*/
/* Extract the comparison type from the gauge. If there is no
comparison type, assume that it is "EQUAL" */
compareparam = icalproperty_get_first_parameter(
p,
ICAL_XLICCOMPARETYPE_PARAMETER);
if (compareparam!=0){
compare = icalparameter_get_xliccomparetype(compareparam);
} else {
compare = ICAL_XLICCOMPARETYPE_EQUAL;
}
/* Find a property in the component that has the same type
as the gauge property. HACK -- multiples of a single
property type in the gauge will match only the first
instance in the component */
targetprop = icalcomponent_get_first_property(comp,
icalproperty_isa(p));
if(targetprop != 0){
/* Compare the values of the gauge property and the target
property */
rel = icalvalue_compare(icalproperty_get_value(p),
icalproperty_get_value(targetprop));
/* Now see if the comparison is equavalent to the comparison
specified in the gauge */
if (rel == compare){
localpass++;
} else if (compare == ICAL_XLICCOMPARETYPE_LESSEQUAL &&
( rel == ICAL_XLICCOMPARETYPE_LESS ||
rel == ICAL_XLICCOMPARETYPE_EQUAL)) {
localpass++;
} else if (compare == ICAL_XLICCOMPARETYPE_GREATEREQUAL &&
( rel == ICAL_XLICCOMPARETYPE_GREATER ||
rel == ICAL_XLICCOMPARETYPE_EQUAL)) {
localpass++;
} else if (compare == ICAL_XLICCOMPARETYPE_NOTEQUAL &&
( rel == ICAL_XLICCOMPARETYPE_GREATER ||
rel == ICAL_XLICCOMPARETYPE_LESS)) {
localpass++;
} else {
localpass = 0;
}
pass = pass && (localpass>0);
}
}
/* Test subcomponents. Look for a child component that has a
counterpart in the gauge. If one is found, recursively call
icaldirset_test */
for(subgauge = icalcomponent_get_first_component(gauge,ICAL_ANY_COMPONENT);
subgauge != 0;
subgauge = icalcomponent_get_next_component(gauge,ICAL_ANY_COMPONENT)){
gaugekind = icalcomponent_isa(subgauge);
if (gaugekind == ICAL_ANY_COMPONENT){
child = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT);
} else {
child = icalcomponent_get_first_component(comp,gaugekind);
}
if(child !=0){
localpass = icalgauge_compare_recurse(child,subgauge);
pass = pass && localpass;
} else {
pass = 0;
}
}
return pass;
}
int icalgauge_compare(icalgauge* gauge,icalcomponent* comp)
{
struct icalgauge_impl *impl = (struct icalgauge_impl*)gauge;
icalcomponent *inner;
int local_pass = 0;
int last_clause = 1, this_clause = 1;
pvl_elem e;
icalerror_check_arg_rz( (comp!=0), "comp");
icalerror_check_arg_rz( (gauge!=0), "gauge");
inner = icalcomponent_get_first_real_component(comp);
if(inner == 0){
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
return 0;
}
/* Check that this component is one of the FROM types */
local_pass = 0;
for(e = pvl_head(impl->from);e!=0;e=pvl_next(e)){
icalcomponent_kind k = (icalcomponent_kind)pvl_data(e);
if(k == icalcomponent_isa(inner)){
local_pass=1;
}
}
if(local_pass == 0){
return 0;
}
/* Check each where clause against the component */
for(e = pvl_head(impl->where);e!=0;e=pvl_next(e)){
struct icalgauge_where *w = pvl_data(e);
icalcomponent *sub_comp;
icalvalue *v;
icalproperty *prop;
icalvalue_kind vk;
if(w->prop == ICAL_NO_PROPERTY || w->value == 0){
icalerror_set_errno(ICAL_INTERNAL_ERROR);
return 0;
}
/* First, create a value from the gauge */
vk = icalenum_property_kind_to_value_kind(w->prop);
if(vk == ICAL_NO_VALUE){
icalerror_set_errno(ICAL_INTERNAL_ERROR);
return 0;
}
v = icalvalue_new_from_string(vk,w->value);
if (v == 0){
/* Keep error set by icalvalue_from-string*/
return 0;
}
/* Now find the corresponding property in the component,
descending into a sub-component if necessary */
if(w->comp == ICAL_NO_COMPONENT){
sub_comp = inner;
} else {
sub_comp = icalcomponent_get_first_component(inner,w->comp);
if(sub_comp == 0){
return 0;
}
}
this_clause = 0;
local_pass = 0;
for(prop = icalcomponent_get_first_property(sub_comp,w->prop);
prop != 0;
prop = icalcomponent_get_next_property(sub_comp,w->prop)){
icalvalue* prop_value;
icalgaugecompare relation;
prop_value = icalproperty_get_value(prop);
relation = (icalgaugecompare)icalvalue_compare(prop_value,v);
if (relation == w->compare){
local_pass++;
} else if (w->compare == ICALGAUGECOMPARE_LESSEQUAL &&
( relation == ICALGAUGECOMPARE_LESS ||
relation == ICALGAUGECOMPARE_EQUAL)) {
local_pass++;
} else if (w->compare == ICALGAUGECOMPARE_GREATEREQUAL &&
( relation == ICALGAUGECOMPARE_GREATER ||
relation == ICALGAUGECOMPARE_EQUAL)) {
local_pass++;
} else if (w->compare == ICALGAUGECOMPARE_NOTEQUAL &&
( relation == ICALGAUGECOMPARE_GREATER ||
relation == ICALGAUGECOMPARE_LESS)) {
local_pass++;
} else {
local_pass = 0;
}
}
this_clause = local_pass > 0 ? 1 : 0;
/* Now look at the logic operator for this clause to see how
the value should be merge with the previous clause */
if(w->logic == ICALGAUGELOGIC_AND){
last_clause = this_clause && last_clause;
} else if(w->logic == ICALGAUGELOGIC_AND) {
last_clause = this_clause || last_clause;
} else {
last_clause = this_clause;
}
}
return last_clause;
}
void icalgauge_dump(icalcomponent* gauge)
{
pvl_elem *p;
struct icalgauge_impl *impl = (struct icalgauge_impl*)gauge;
printf("--- Select ---\n");
for(p = pvl_head(impl->select);p!=0;p=pvl_next(p)){
struct icalgauge_where *w = pvl_data(p);
if(w->comp != ICAL_NO_COMPONENT){
printf("%s ",icalenum_component_kind_to_string(w->comp));
}
if(w->prop != ICAL_NO_PROPERTY){
printf("%s ",icalenum_property_kind_to_string(w->prop));
}
if (w->compare != ICALGAUGECOMPARE_NONE){
printf("%d ",w->compare);
}
if (w->value!=0){
printf("%s",w->value);
}
printf("\n");
}
printf("--- From ---\n");
for(p = pvl_head(impl->from);p!=0;p=pvl_next(p)){
icalcomponent_kind k = (icalcomponent_kind)pvl_data(p);
printf("%s\n",icalenum_component_kind_to_string(k));
}
printf("--- Where ---\n");
for(p = pvl_head(impl->where);p!=0;p=pvl_next(p)){
struct icalgauge_where *w = pvl_data(p);
if(w->logic != ICALGAUGELOGIC_NONE){
printf("%d ",w->logic);
}
if(w->comp != ICAL_NO_COMPONENT){
printf("%s ",icalenum_component_kind_to_string(w->comp));
}
if(w->prop != ICAL_NO_PROPERTY){
printf("%s ",icalenum_property_kind_to_string(w->prop));
}
if (w->compare != ICALGAUGECOMPARE_NONE){
printf("%d ",w->compare);
}
if (w->value!=0){
printf("%s",w->value);
}
printf("\n");
}
}