/********************************************************************** * * filter.cpp -- * Copyright (C) 1999 The New Zealand Digital Library Project * * A component of the Greenstone digital library software * from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *********************************************************************/ #include "filter.h" #include "fileutil.h" #include // default constructor does nothing filterclass::filterclass () { } // default destructor does nothing filterclass::~filterclass () { } // configure should be called once for each configuration line // default configures the default filter options void filterclass::configure (const text_t &key, const text_tarray &cfgline) { if (cfgline.size() >= 1) { const text_t &value = cfgline[0]; if (key == "collection") collection = value; else if (key == "collectdir") collectdir = value; else if (key == "gsdlhome") gsdlhome = value; else if (key == "gdbmhome") dbhome = value; else if ((key == "filteroptdefault") && (cfgline.size() == 2)) { // see if this filter has an option with this name FilterOption_tmap::iterator thisfilteroption = filterOptions.find(cfgline[0]); if (thisfilteroption != filterOptions.end()) (*thisfilteroption).second.defaultValue = cfgline[1]; } } } // init should be called after all the configuration is done but // before any other methods are called // default checks all the filter option defaults bool filterclass::init (ostream &/*logout*/) { // check all the filter defaults FilterOption_tmap::iterator filteroption_here = filterOptions.begin(); FilterOption_tmap::iterator filteroption_end = filterOptions.end(); while (filteroption_here != filteroption_end) { (*filteroption_here).second.check_defaultValue (); ++filteroption_here; } if (dbhome.empty()) dbhome = gsdlhome; // get the collection directory name if (collectdir.empty()) { collectdir = filename_cat (gsdlhome, "collect", collection); } return true; } // returns the name of this filter // default returns "NullFilter" text_t filterclass::get_filter_name () { return "NullFilter"; } // returns the current filter options void filterclass::get_filteroptions (InfoFilterOptionsResponse_t &response, comerror_t &err, ostream &/*logout*/) { response.clear(); response.filterOptions = filterOptions; err = noError; } // default returns nothing void filterclass::filter (const FilterRequest_t &request, FilterResponse_t &response, comerror_t &err, ostream &/*logout*/) { ResultDocInfo_t resultdoc; response.clear(); if ((request.filterResultOptions & FROID) || (request.filterResultOptions & FRmetadata)) { // copy the OIDs from the request to the response text_tarray::const_iterator here = request.docSet.begin(); text_tarray::const_iterator end = request.docSet.end(); while (here != end) { resultdoc.OID = (*here); response.docInfo.push_back(resultdoc); ++here; } } response.numDocs = response.docInfo.size(); response.isApprox = Exact; err = noError; } bool operator==(const filterptr &x, const filterptr &y) { return (x.f == y.f); } bool operator<(const filterptr &x, const filterptr &y) { return (x.f < y.f); } // thefilter remains the property of the calling code but // should not be deleted until it is removed from this list. void filtermapclass::addfilter (filterclass *thefilter) { // can't add a null filter assert (thefilter != NULL); if (thefilter == NULL) return; // can't add an filter with no name assert (!(thefilter->get_filter_name()).empty()); if ((thefilter->get_filter_name()).empty()) return; filterptr fptr; fptr.f = thefilter; filterptrs[thefilter->get_filter_name()] = fptr; } // getfilter will return NULL if the filter could not be found filterclass *filtermapclass::getfilter (const text_t &key) { // can't find an filter with no name assert (!key.empty()); if (key.empty()) return NULL; iterator here = filterptrs.find (key); if (here == filterptrs.end()) return NULL; return (*here).second.f; } // some useful functions for dealing with document sets // returns -1 if t1 is a child of t2 // returns 0 if t1 and t2 are not parent-child related // returns 1 if t1 is a parent of t2 int child_compare (const text_t &t1, const text_t &t2) { text_t::const_iterator t1_here = t1.begin(); text_t::const_iterator t1_end = t1.end(); text_t::const_iterator t2_here = t2.begin(); text_t::const_iterator t2_end = t2.end(); while ((t1_here != t1_end) && (t2_here != t2_end)) { if (*t1_here != *t2_here) return 0; // unrelated ++t1_here; ++t2_here; } if ((t1_here == t1_end) && (t2_here == t2_end)) return 0; // equal if (t1_here != t1_end) { if (*t1_here == '.') return -1; // t1 is child else return 0; // unrelated } if (t2_here != t2_end) { if (*t2_here == '.') return 1; // t2 is child else return 0; // unrelated } return 0; // shouldn't get here... } // intersect places the result in set1 void intersect (text_tset &set1, const text_tset &set2) { text_tset resultset; int childcomp = 0; text_tset::const_iterator set1_here = set1.begin(); text_tset::const_iterator set1_end = set1.end(); text_tset::const_iterator set2_here = set2.begin(); text_tset::const_iterator set2_end = set2.end(); while ((set1_here != set1_end) && (set2_here != set2_end)) { if (*set1_here == *set2_here) { // equal resultset.insert (*set1_here); ++set1_here; ++set2_here; } else if ((childcomp=child_compare(*set1_here, *set2_here)) != 0) { if (childcomp < 0) { // set1_here is child resultset.insert (*set1_here); ++set1_here; } else { // set2_here is child resultset.insert (*set2_here); ++set2_here; } } else if (*set1_here < *set2_here) { // set1 is less ++set1_here; } else { // set2 is less ++set2_here; } } set1 = resultset; } void intersect (text_tarray &set1, const text_tset &set2) { text_tarray resultset; text_tarray::const_iterator set1_here = set1.begin(); text_tarray::const_iterator set1_end = set1.end(); while (set1_here != set1_end) { if (in_set (set2, *set1_here)) resultset.push_back (*set1_here); ++set1_here; } set1 = resultset; } void intersect (text_tarray &set1, const text_tarray &set2) { text_tarray resultset; text_tarray::const_iterator set1_here = set1.begin(); text_tarray::const_iterator set1_end = set1.end(); while (set1_here != set1_end) { if (in_set (set2, *set1_here)) resultset.push_back (*set1_here); ++set1_here; } set1 = resultset; } // tests to see if el is in set bool in_set (const text_tset &set1, const text_t &el) { text_t::const_iterator here = el.begin(); text_t::const_iterator end = el.end(); text_t tryel, tryel_add; bool first = true; // the element is in the set if any of its parents are // in the set do { // get next possible element to try here = getdelimitstr (here, end, '.', tryel_add); if (!first) tryel += "."; first = false; tryel += tryel_add; // see if this element is in the set if (set1.find(tryel) != set1.end()) return true; } while (here != end); return false; } bool in_set (const text_tarray &set1, const text_t &el) { text_t::const_iterator here = el.begin(); text_t::const_iterator end = el.end(); text_t tryel, tryel_add; bool first = true; // the element is in the set if any of its parents are // in the set do { // get next possible element to try here = getdelimitstr (here, end, '.', tryel_add); if (!first) tryel += "."; first = false; tryel += tryel_add; // see if this element is in the set text_tarray::const_iterator h = set1.begin(); text_tarray::const_iterator e = set1.end(); while (h != e) { if (*h == tryel) return true; ++h; } } while (here != end); return false; }