00001 /* 00002 * objtree.h 00003 * 00004 * Copyright (C) 2006,2008 Thomas A. Vaughan 00005 * All rights reserved. 00006 * 00007 * 00008 * Redistribution and use in source and binary forms, with or without 00009 * modification, are permitted provided that the following conditions are met: 00010 * * Redistributions of source code must retain the above copyright 00011 * notice, this list of conditions and the following disclaimer. 00012 * * Redistributions in binary form must reproduce the above copyright 00013 * notice, this list of conditions and the following disclaimer in the 00014 * documentation and/or other materials provided with the distribution. 00015 * * Neither the name of the <organization> nor the 00016 * names of its contributors may be used to endorse or promote products 00017 * derived from this software without specific prior written permission. 00018 * 00019 * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY 00020 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00022 * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY 00023 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 00026 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00029 */ 00030 00031 #ifndef WAVEPACKET_OBJTREE_H__ 00032 #define WAVEPACKET_OBJTREE_H__ 00033 00034 00035 // includes -------------------------------------------------------------------- 00036 #include "common/common.h" 00037 #include "datahash/datahash.h" 00038 00039 /// \ingroup general 00040 /*@{*/ 00041 00042 //////////////////////////////////////////////////////////////////////////////// 00043 /// 00044 /// \defgroup objtree Object Tree API 00045 /// 00046 /// API for manipulating hierarchical trees of objects + properties. 00047 /// 00048 /// This is typically used by UI applications when the user has selected a 00049 /// number of different object, and then wants to see/edit properties. This 00050 /// low-level API abstracts the general (hierarchical) set of objects. 00051 /// 00052 //////////////////////////////////////////////////////////////////////////////// 00053 00054 /*@{*/ 00055 00056 00057 namespace objtree { 00058 00059 00060 /// returns true if the given name is an acceptable property name 00061 /// 00062 /// property names are very restricted! alphanumerics and hyphens only 00063 /// goal is names that do not need to be URL encoded etc 00064 bool isValidPropertyName(IN const char * candidate_name) throw(); 00065 00066 00067 /// abstraction of a general property of an object 00068 /// 00069 /// This attempts to capture the metadata around a property. For instance, 00070 /// the user may have selected a rectangle, and they want the properties of 00071 /// the rectangle. A single property of the rectangle (for instance the 00072 /// line color) will be generally represented by this. The "value" of the 00073 /// property is the line color (red, black, whatever). But there could be 00074 /// other metadata fields such as the property type (it is a color, not a 00075 /// weight or length etc.). It could have units (lengths could be measured 00076 /// in meters or miles, etc.). And so on. 00077 /// 00078 /// There are multiple 00079 /// possible metadata fields (up to the client), such as the type (string? 00080 /// float? int? color?), units (centimeters? miles? liters?) etc. 00081 /// 00082 /// There is one "special" property, the value. All property_t objects are 00083 /// required to have a value entry, albeit empty. All other fields may 00084 /// not exist (and getFieldValue() can return null for those). But 00085 /// clients can rely on getValue() returning non-null at least. 00086 struct property_t : public Dictionary { 00087 // constructor, manipulators 00088 property_t(IN const char * in_value) { 00089 ASSERT(in_value, "null input value"); 00090 this->setValue(in_value); 00091 } 00092 property_t(void) throw() { this->clear(); } 00093 00094 void clear(void) throw() { 00095 Dictionary::clear(); 00096 this->setValue(NULL); 00097 } 00098 00099 void setFieldValue(IN const char * field, IN const char * value) { 00100 ASSERT(field, "null field"); 00101 ASSERT(value, "null value"); 00102 ASSERT(strcmp(field, "name"), 00103 "'name' is a reserved property field value"); 00104 this->operator[](field) = value; 00105 } 00106 00107 const char * getFieldValue(IN const char * field) const throw() { 00108 ASSERT(field, "null field"); 00109 Dictionary::const_iterator i = this->find(field); 00110 if (this->end() == i) 00111 return NULL; // field not found 00112 return i->second.c_str(); 00113 } 00114 00115 const char * getOptionalFieldValue(IN const char * field, 00116 IN const char * default_value) const throw() { 00117 const char * val = this->getFieldValue(field); 00118 return (val) ? val : default_value; 00119 } 00120 00121 void setValue(IN const char * value) throw() { 00122 if (!value) 00123 value = ""; 00124 this->setFieldValue("value", value); 00125 } 00126 00127 const char * getValue(void) const throw() { 00128 return this->getFieldValue("value"); 00129 } 00130 00131 void dump(IN const char * title) const throw(); 00132 }; 00133 00134 00135 00136 /// a set of properties. 00137 /// 00138 /// For instance, a rectangle object could have a propert set which contained 00139 /// a property "line_color", "line_style", "fill_color", etc. 00140 struct property_set_t : public std::map<std::string, property_t> { 00141 // manipulators 00142 void addProperty(IN const char * name, 00143 IN const char * value) { 00144 ASSERT(isValidPropertyName(name), 00145 "Bad property name: '%s'", name); 00146 (*this)[name] = property_t(value); 00147 } 00148 00149 property_t * getProperty(IN const char * name) throw() { 00150 ASSERT(isValidPropertyName(name), 00151 "Bad property name: '%s'", name); 00152 property_set_t::iterator i = this->find(name); 00153 if (this->end() == i) 00154 return NULL; // property not found 00155 return &i->second; 00156 } 00157 00158 const property_t * getProperty(IN const char * name) const throw() { 00159 ASSERT(isValidPropertyName(name), 00160 "Bad property name: '%s'", name); 00161 property_set_t::const_iterator i = this->find(name); 00162 if (this->end() == i) 00163 return NULL; // property not found 00164 return &i->second; 00165 } 00166 00167 const char * getValue(IN const char * name) const throw() { 00168 ASSERT(isValidPropertyName(name), 00169 "Bad property name: '%s'", name); 00170 const property_t * p = this->getProperty(name); 00171 if (!p) 00172 return NULL; // not found 00173 return p->getValue(); 00174 } 00175 00176 const char * getOptionalValue(IN const char * name, 00177 IN const char * default_value) const throw() { 00178 const char * value = this->getValue(name); 00179 return value ? value : default_value; 00180 } 00181 00182 void dump(IN const char * title) const throw(); 00183 }; 00184 00185 00186 00187 //////////////////////////////////////////////////////////////////////////////// 00188 /// 00189 /// How you interact with this object is the key to the objtree library. 00190 /// 00191 /// You can add and remove objects by their (client-specified) ID. 00192 /// 00193 /// You can then iteract with multiple objects at once by specifying the 00194 /// targets as an id_query rather than a single ID. For instance, you could 00195 /// call 00196 /// \code 00197 /// list->setProperties("*", my_property_set); 00198 /// \endcode 00199 /// which would update that property for all objects in the list, provided 00200 /// they already had a property of that name. 00201 /// 00202 /// NOTE: query support is a bit limited. At the moment you can only use 00203 /// a specific ID, or the query "*" which specifies all IDs in the list. 00204 /// Better query support could obviously be added but I haven't needed it. 00205 /// 00206 //////////////////////////////////////////////////////////////////////////////// 00207 class List { 00208 public: 00209 // virtual destructor -------------------------------------------------- 00210 virtual ~List(void) throw(); 00211 00212 // objtree::List class interface methods ------------------------------- 00213 virtual bool addObject(IN const char * id) = 0; 00214 virtual bool removeObject(IN const char * id) = 0; 00215 virtual void getObjects(IN const char * id_query, 00216 OUT VecString& ids) = 0; 00217 00218 /// getProperties -- retrieves properties common to all objects 00219 virtual void getProperties(IN const char * id_query, 00220 OUT property_set_t& pset) = 0; 00221 00222 /// addProperties -- adds if necessary, sets all properties on all objs 00223 virtual void addProperties(IN const char * id_query, 00224 IN const property_set_t& pset) = 0; 00225 00226 /// setProperties -- sets properties only if exists + matches on object 00227 virtual void setProperties(IN const char * id_query, 00228 IN const property_set_t& pset) = 0; 00229 00230 // public static methods (factories) ----------------------------------- 00231 static smart_ptr<List> create(void); 00232 }; 00233 00234 00235 00236 /// the root object for discovering objects 00237 /// 00238 /// Why isn't this recursive? Because the lists already contain recursive 00239 /// structure (you can use nested object IDs and treat them as paths). 00240 /// 00241 /// So the List could properly be described as a Tree, and this is really a 00242 /// Forest. 00243 class Tree { 00244 public: 00245 // virtual destructor -------------------------------------------------- 00246 virtual ~Tree(void) throw(); 00247 00248 // objtree::Tree class interface methods ------------------------------- 00249 /// return the list (and create if no list exists yet by that name) 00250 virtual List * getList(IN const char * name) = 0; 00251 00252 /// return names of all lists 00253 virtual void getListNames(OUT VecString& names) = 0; 00254 00255 // public static methods (factories) ----------------------------------- 00256 static smart_ptr<Tree> create(void); 00257 }; 00258 00259 00260 00261 //////////////////////////////////////////////////////////////////////////////// 00262 // 00263 // helper methods 00264 // 00265 //////////////////////////////////////////////////////////////////////////////// 00266 00267 bool isTrue(IN const char * text) throw(); 00268 00269 /// given a property set, serialize into a datahash object 00270 smart_ptr<Datahash> getDatahash(IN const property_set_t& pset); 00271 00272 /// given a datahash, deserialize into a property set 00273 void getPropertySet(IN const Datahash * hash, 00274 OUT property_set_t& pset); 00275 00276 00277 }; // objtree namespace 00278 00279 #endif // WAVEPACKET_OBJTREE_H__ 00280