uniconfkey.cc

00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 2002 Net Integration Technologies, Inc.
00004  *
00005  * A UniConf hierarchical key path abstraction.
00006  */
00007 #include "wvassert.h"
00008 #include "wvstream.h"
00009 #include "uniconfkey.h"
00010 #include <climits>
00011 #include <assert.h>
00012 #include <strutils.h>
00013 
00014 UniConfKey UniConfKey::EMPTY;
00015 UniConfKey UniConfKey::ANY("*");
00016 UniConfKey UniConfKey::RECURSIVE_ANY("...");
00017 
00018 
00019 UniConfKey::UniConfKey() :
00020     path("") // important to ensure we don't get nil paths everywhere
00021              // since for the moment we are not equipped to deal with them
00022 {
00023 }
00024 
00025 
00026 void UniConfKey::init(WvStringParm key)
00027 {
00028     assert(!key.isnull());
00029 
00030     // canonicalize the key by removing leading/trailing slashes, and
00031     // changing multiple slashes in a row to single slashes.
00032 
00033     if (key[0] == '/' || *(strchr(key, 0)-1) == '/' || strstr(key, "//"))
00034     {
00035         path.setsize(strlen(key) + 1);
00036         char *optr = path.edit();
00037         const char *iptr = key + int(strspn(key, "/"));
00038         
00039         while (*iptr)
00040         {
00041             if (*iptr == '/')
00042             {
00043                 // if there's more than one slash, this finds the last one:
00044                 iptr += strspn(iptr, "/") - 1;
00045                 
00046                 // if there's nothing after the slash, it's a terminating
00047                 // slash.  copy the terminating slash and then stop.
00048                 if (!iptr[1])
00049                 {
00050                     *optr++ = *iptr++;
00051                     break;
00052                 }
00053                 
00054                 // if we get here, it's exactly one intermediate slash.
00055             }
00056             
00057             *optr++ = *iptr++;
00058         }
00059         
00060         *optr = 0;
00061     }
00062     else // easy: already in good shape!  Use WvString's optimized copying.
00063         path = key;
00064 }
00065 
00066 
00067 UniConfKey::UniConfKey(const UniConfKey &other) : path(other.path)
00068 {
00069 }
00070 
00071 
00072 UniConfKey::UniConfKey(const UniConfKey &_path, const UniConfKey &_key) 
00073 {
00074     if (!_path.path)
00075         path = _key;
00076     else
00077         path = spacecat(_path, _key, '/', true);
00078 }
00079 
00080 
00081 void UniConfKey::append(const UniConfKey &_key)
00082 {
00083     if (!path)
00084         path = _key.path;
00085     else
00086         path = spacecat(path, _key.path, '/', true);
00087 }
00088 
00089 
00090 void UniConfKey::prepend(const UniConfKey &_key)
00091 {
00092     if (!path)
00093         path = _key.path;
00094     else if (!!_key.path)
00095         path = spacecat(_key.path, path, '/', true);
00096 }
00097 
00098 
00099 bool UniConfKey::isempty() const
00100 {
00101     return !path;
00102 }
00103 
00104 
00105 bool UniConfKey::iswild() const
00106 {
00107     // FIXME: not precise
00108     return strchr(path, '*') || strstr(path, "...");
00109 }
00110 
00111 
00112 bool UniConfKey::hastrailingslash() const
00113 {
00114     const char *s = path.cstr();
00115     return s[strlen(s) - 1] == '/';
00116 }
00117 
00118 
00119 int UniConfKey::numsegments() const
00120 {
00121     if (!path)
00122         return 0;
00123     
00124     int n = 1; // all non-null paths have at least one segment
00125     for (const char *cptr = path; *cptr; cptr++)
00126     {
00127         if (*cptr == '/')
00128             n++;
00129     }
00130     return n;
00131 }
00132 
00133 
00134 UniConfKey UniConfKey::segment(int n) const
00135 {
00136     return range(n, n + 1);
00137 }
00138 
00139 
00140 UniConfKey UniConfKey::pop(int n)
00141 {
00142     UniConfKey res = range(0,n);
00143     *this = range(n, INT_MAX);
00144     return res;
00145 }
00146 
00147 
00148 UniConfKey UniConfKey::first(int n) const
00149 {
00150     return range(0, n);
00151 }
00152 
00153 
00154 UniConfKey UniConfKey::last(int n) const
00155 {
00156     return range(numsegments() - n, INT_MAX);
00157 }
00158 
00159 
00160 UniConfKey UniConfKey::removefirst(int n) const
00161 {
00162     return range(n, INT_MAX);
00163 }
00164 
00165 
00166 UniConfKey UniConfKey::removelast(int n) const
00167 {
00168     return range(0, numsegments() - n);
00169 }
00170 
00171 
00172 UniConfKey UniConfKey::range(int i, int j) const
00173 {
00174     if (!path) return *this;
00175     
00176     const char *sptr, *eptr;
00177     int count;
00178     
00179     // find the beginning
00180     for (sptr = path, count = 0; *sptr && count < i; sptr++)
00181     {
00182         if (*sptr == '/')
00183             count++;
00184     }
00185     
00186     // find the end
00187     for (eptr = sptr; *eptr; eptr++)
00188     {
00189         if (*eptr == '/')
00190             count++;
00191         
00192         if (count >= j)
00193             break; // don't want to increment eptr
00194     }
00195     
00196     // optimization: they got the whole key!  Don't copy.
00197     if (sptr == path && !*eptr)
00198         return *this;
00199 
00200     // otherwise, return a new key.
00201     UniConfKey result; // avoid running the normalizing constructor
00202     int len = eptr - sptr;
00203     if (len)
00204     {
00205         result.path.setsize(len + 1);
00206         char *cptr = result.path.edit();
00207         strncpy(cptr, sptr, len);
00208         cptr[len] = 0;
00209     }
00210     return result;
00211 }
00212 
00213 
00214 WvString UniConfKey::printable() const
00215 {
00216     return path;
00217 }
00218 
00219 
00220 UniConfKey &UniConfKey::operator= (const UniConfKey &other)
00221 {
00222     path = other.path;
00223     return *this;
00224 }
00225 
00226 
00227 int UniConfKey::compareto(const UniConfKey &other) const
00228 {
00229     return strcasecmp(path, other.path);
00230 }
00231 
00232 
00233 bool UniConfKey::matches(const UniConfKey &pattern) const
00234 {
00235     // TODO: optimize this function
00236     if (*this == pattern)
00237         return true;
00238     
00239     UniConfKey head(pattern.first());
00240 
00241     // handle * wildcard
00242     if (head == UniConfKey::ANY)
00243     {
00244         if (isempty())
00245             return false;
00246         return removefirst().matches(pattern.removefirst());
00247     }
00248 
00249     // handle ... wildcard
00250     if (head == UniConfKey::RECURSIVE_ANY)
00251     {
00252         UniConfKey tail(pattern.removefirst());
00253         if (tail.isempty())
00254             return true; // recursively matches anything
00255         for (int n = 0; ; ++n)
00256         {
00257             UniConfKey part(removefirst(n));
00258             if (part.matches(tail))
00259                 return true;
00260             if (part.isempty())
00261                 break;
00262         }
00263         return false;
00264     }
00265     
00266     // no other wildcard arrangements currently supported
00267     return false;
00268 }
00269 
00270 
00271 bool UniConfKey::suborsame(const UniConfKey &key) const
00272 {
00273     int n = numsegments();
00274     if (hastrailingslash())
00275         n -= 1;
00276 
00277     UniConfKey k = key.first(numsegments());
00278 
00279     if (key.first(n) == first(n))
00280         return true;
00281     return false;
00282 }
00283 
00284 
00285 bool UniConfKey::suborsame(const UniConfKey &key, WvString &subkey) const
00286 {
00287     int n = numsegments();
00288 
00289     // Compensate for the directory-style naming convention of the
00290     // trailing slash.
00291     if (hastrailingslash())
00292         n -= 1;
00293 
00294     if (key.first(n) == first(n))
00295     {
00296         subkey = key.removefirst(n);
00297         return true;
00298     }
00299     return false;
00300 }
00301 
00302 
00303 UniConfKey UniConfKey::subkey(const UniConfKey &key) const
00304 {
00305     WvString answer;
00306     wvassert(suborsame(key, answer),
00307              "this = '%s'\nkey = '%s'", printable(), key);
00308     return answer;
00309 }

Generated on Thu May 25 21:51:02 2006 for WvStreams by  doxygen 1.4.6