WvStreams
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 "wvhash.h"
00011 #include <climits>
00012 #include <assert.h>
00013 #include <strutils.h>
00014 
00015 unsigned WvHash(const UniConfKey &k)
00016 {
00017     int numsegs = k.right - k.left;
00018     unsigned result;
00019     switch (numsegs)
00020     {
00021         case 0:
00022             result = 0;
00023             break;
00024         case 1:
00025             result = WvHash(k.store->segments[k.left]);
00026             break;
00027         default:
00028             result = WvHash(k.store->segments[k.left])
00029                 ^ WvHash(k.store->segments[k.right - 1])
00030                 ^ numsegs;
00031             break;
00032     }
00033     return result;
00034 }
00035 
00036 // The initial value of 1 for the ref_count of these guarantees
00037 // that they won't ever be deleted
00038 UniConfKey::Store UniConfKey::EMPTY_store(1, 1);
00039 UniConfKey::Store UniConfKey::ANY_store(1, 1, "*");
00040 UniConfKey::Store UniConfKey::RECURSIVE_ANY_store(1, 1, "...");
00041 
00042 UniConfKey UniConfKey::EMPTY(&EMPTY_store, 0, 0);
00043 UniConfKey UniConfKey::ANY(&ANY_store, 0, 1);
00044 UniConfKey UniConfKey::RECURSIVE_ANY(&RECURSIVE_ANY_store, 0, 1);
00045 
00046 
00047 UniConfKey::Store::Store(int size, int _ref_count,
00048         WvStringParm key) :
00049     segments(size),
00050     ref_count(_ref_count)
00051 {
00052     if (!key)
00053         return;
00054 
00055     WvStringList parts;
00056     parts.split(key, "/");
00057 
00058     segments.resize(parts.count());
00059     WvStringList::Iter part(parts);
00060     for (part.rewind(); part.next(); )
00061     {
00062         if (!*part)
00063             continue;
00064         segments.append(*part);
00065     }
00066     if (!!key && key[key.len()-1] == '/' && segments.used() > 0)
00067         segments.append(Segment());
00068 }
00069 
00070 
00071 UniConfKey &UniConfKey::collapse()
00072 {
00073     if ((right - left == 1 && !store->segments[right-1])
00074         || right == left)
00075     {
00076         if (--store->ref_count == 0)
00077             delete store;
00078         store = &EMPTY_store;
00079         left = right = 0;
00080         ++store->ref_count;
00081     }
00082     return *this;
00083 }
00084  
00085 
00086 void UniConfKey::unique()
00087 {
00088     if (store->ref_count == 1)
00089         return;
00090     store->ref_count--;
00091     Store *old_store = store;
00092     store = new Store(right - left, 1);
00093     for (int i=left; i<right; ++i)
00094         store->segments.append(old_store->segments[i]);
00095     right -= left;
00096     left = 0;
00097 }
00098     
00099 UniConfKey::UniConfKey(const UniConfKey &_path, const UniConfKey &_key) :
00100     store(new Store(_path.numsegments() + _key.numsegments() + 1, 1)),
00101     left(0),
00102     right(0)
00103 {
00104     bool hastrailingslash = _key.isempty() || _key.hastrailingslash();
00105     for (int i=_path.left; i<_path.right; ++i)
00106     {
00107         const Segment &segment = _path.store->segments[i];
00108         if (!segment)
00109             continue;
00110         store->segments.append(segment);
00111         ++right;
00112     }
00113     for (int j=_key.left; j<_key.right; ++j)
00114     {
00115         const Segment &segment = _key.store->segments[j];
00116         if (!segment)
00117             continue;
00118         store->segments.append(segment);
00119         ++right;
00120     }
00121     if (hastrailingslash)
00122     {
00123         store->segments.append("");
00124         ++right;
00125     }
00126     collapse();
00127 }
00128 
00129 
00130 void UniConfKey::append(const UniConfKey &_key)
00131 {
00132     bool hastrailingslash = _key.isempty() || _key.hastrailingslash();
00133     unique();
00134     store->segments.resize(right - left + _key.right - _key.left + 1);
00135     for (int j=_key.left; j<_key.right; ++j)
00136     {
00137         const Segment &segment = _key.store->segments[j];
00138         if (!segment)
00139             continue;
00140         store->segments.replace(right, segment);
00141         ++right;
00142     }
00143     if (hastrailingslash)
00144     {
00145         store->segments.replace(right, "");
00146         ++right;
00147     }
00148     collapse();
00149 }
00150 
00151 
00152 void UniConfKey::prepend(const UniConfKey &_key)
00153 {
00154     unique();
00155     int shift = 0;
00156     for (int j=_key.left; j<_key.right; ++j)
00157     {
00158         if (!!_key.store->segments[j])
00159             ++shift;
00160     }
00161     store->segments.resize(shift + right - left, shift);
00162     for (int j=_key.left; j<_key.right; ++j)
00163     {
00164         const Segment &segment = _key.store->segments[j];
00165         if (!segment)
00166             continue;
00167         store->segments.replace(left + j - _key.left, segment);
00168         ++right;
00169     }
00170     collapse();
00171 }
00172 
00173 
00174 bool UniConfKey::iswild() const
00175 {
00176     for (int i=left; i<right; ++i)
00177         if (store->segments[i].iswild())
00178             return true;
00179     return false;
00180 }
00181 
00182 
00183 UniConfKey UniConfKey::pop(int n)
00184 {
00185     if (n == 0)
00186         return UniConfKey();
00187     unique();
00188     if (n > right - left)
00189         n = right - left;
00190     if (n < 0)
00191         n = 0;
00192     int old_left = left;
00193     left += n;
00194     UniConfKey result(store, old_left, left);
00195     collapse();
00196     return result.collapse();
00197 }
00198 
00199 
00200 UniConfKey UniConfKey::range(int i, int j) const
00201 {
00202     if (j > right - left)
00203         j = right - left;
00204     if (i < 0)
00205         i = 0;
00206     if (j < i)
00207         j = i;
00208     return UniConfKey(store, left + i, left + j).collapse();
00209 }
00210 
00211 
00212 WvString UniConfKey::printable() const
00213 {
00214     switch (right - left)
00215     {
00216         case 0:
00217             return WvString::empty;
00218         case 1:
00219             return store->segments[left];
00220         default:
00221         {
00222             WvDynBuf buf;
00223             for (int i=left; i<right; ++i)
00224             {
00225                 buf.putstr(store->segments[i]);
00226                 if (i < right-1)
00227                     buf.put('/');
00228             }
00229             return buf.getstr();
00230         }
00231     }
00232 }
00233 
00234 
00235 int UniConfKey::compareto(const UniConfKey &other) const
00236 {
00237     int i, j;
00238     for (i=left, j=other.left; i<right && j<other.right; ++i, ++j)
00239     {
00240         int val = strcasecmp(store->segments[i], other.store->segments[j]);
00241         if (val != 0)
00242             return val;
00243     }
00244     if (i == right)
00245     {
00246         if (j == other.right)
00247             return 0;
00248         else
00249             return -1;
00250     }
00251     else
00252         return 1;
00253 }
00254 
00255 
00256 bool UniConfKey::matches(const UniConfKey &pattern) const
00257 {
00258     // TODO: optimize this function
00259     if (*this == pattern)
00260         return true;
00261     
00262     UniConfKey head(pattern.first());
00263 
00264     // handle * wildcard
00265     if (head == UniConfKey::ANY)
00266     {
00267         if (isempty())
00268             return false;
00269         return removefirst().matches(pattern.removefirst());
00270     }
00271 
00272     // handle ... wildcard
00273     if (head == UniConfKey::RECURSIVE_ANY)
00274     {
00275         UniConfKey tail(pattern.removefirst());
00276         if (tail.isempty())
00277             return true; // recursively matches anything
00278         for (int n = 0; ; ++n)
00279         {
00280             UniConfKey part(removefirst(n));
00281             if (part.matches(tail))
00282                 return true;
00283             if (part.isempty())
00284                 break;
00285         }
00286         return false;
00287     }
00288     
00289     // no other wildcard arrangements currently supported
00290     return false;
00291 }
00292 
00293 
00294 bool UniConfKey::suborsame(const UniConfKey &key) const
00295 {
00296     int n = numsegments();
00297     if (hastrailingslash())
00298         n -= 1;
00299 
00300     if (key.first(n) == first(n))
00301         return true;
00302     return false;
00303 }
00304 
00305 
00306 bool UniConfKey::suborsame(const UniConfKey &key, UniConfKey &subkey) const
00307 {
00308     int n = numsegments();
00309 
00310     // Compensate for the directory-style naming convention of the
00311     // trailing slash.
00312     if (hastrailingslash())
00313         n -= 1;
00314 
00315     if (key.first(n) == first(n))
00316     {
00317         subkey = key.removefirst(n);
00318         return true;
00319     }
00320     return false;
00321 }
00322 
00323 
00324 UniConfKey UniConfKey::subkey(const UniConfKey &key) const
00325 {
00326     UniConfKey answer;
00327     wvassert(suborsame(key, answer),
00328              "this = '%s'\nkey = '%s'", printable(), key);
00329     return answer;
00330 }