00001 /* 00002 * HeyParser.hh 00003 * 00004 * Copyright (c) 2003, 2004 The University of Utah and the Flux Group. 00005 * All rights reserved. 00006 * 00007 * This file is licensed under the terms of the GNU Public License. 00008 * See the file "license.terms" for restrictions on redistribution 00009 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 00010 */ 00011 00012 /** 00013 * @file HeyParser.hh 00014 * 00015 * Header file for the HeyParser, HeyParserException, and HeyPropertyInfo 00016 * classes. 00017 */ 00018 00019 #ifndef _hey_parser_hh 00020 #define _hey_parser_hh 00021 00022 #include <stack> 00023 #include <iostream> 00024 00025 using namespace std; 00026 00027 /** 00028 * Base class for exceptions thrown by the parser. 00029 * 00030 * @sa HeyParser 00031 */ 00032 class HeyParserException 00033 { 00034 00035 public: 00036 00037 /** 00038 * Construct a HeyParserException object with an optional descriptive 00039 * message. 00040 * 00041 * @param msg A short message describing the problem. 00042 * @param value The value that caused the problem. 00043 */ 00044 HeyParserException(const char *msg = NULL, 00045 const char *value = NULL) 00046 { 00047 this->hpe_Message = msg; 00048 this->hpe_Value = value; 00049 }; 00050 00051 /** 00052 * @return The descriptive message passed into the constructor. 00053 */ 00054 const char *getMessage(void) const 00055 { 00056 return this->hpe_Message; 00057 }; 00058 00059 /** 00060 * @return The value passed into the constructor. 00061 */ 00062 const char *getValue(void) const 00063 { 00064 return this->hpe_Value; 00065 }; 00066 00067 /** 00068 * Output stream operator for HeyParserException objects. Currently just 00069 * prints out the message passed into the constructor. 00070 * 00071 * @param os The destination output stream. 00072 * @param hpe The object to output. 00073 * @return The value of the 'os' parameter. 00074 */ 00075 friend std::ostream &operator<<(std::ostream &os, 00076 const HeyParserException &hpe) 00077 { 00078 return os << (hpe.hpe_Message != NULL ? hpe.hpe_Message : "") 00079 << (hpe.hpe_Value != NULL ? ": " : "") 00080 << (hpe.hpe_Value != NULL ? hpe.hpe_Value : ""); 00081 }; 00082 00083 private: 00084 00085 /** 00086 * A short message describing the problem. 00087 */ 00088 const char *hpe_Message; 00089 00090 /** 00091 * The value that caused the problem. 00092 */ 00093 const char *hpe_Value; 00094 00095 }; 00096 00097 /** 00098 * A parser for command-line arguments given in the pseudo-English "hey" form. 00099 * The syntax for the arguments looks like this: 00100 * 00101 * @li hey @<server@> list @<property@> 00102 * @li hey @<server@> get @<property@> of @<object@> 00103 * @li hey @<server@> set @<property@> of @<object@> to @<value@> 00104 * @li hey @<server@> create @<property@> of @<object@> with @<name@> @<value@> 00105 * @li hey @<server@> delete @<property@> of @<object@> 00106 * @li hey @<server@> execute @<property@> of @<object@> with @<name@> @<value@> 00107 * @li hey @<server@> getsuites of @<object@> 00108 * @li hey @<server@> shutdown 00109 * 00110 * Where: 00111 * 00112 * @li @<server@> is a reference to a running server program, 00113 * @li list/get/etc is the verb describing the action to take, 00114 * @li @<property@> is a object or an attribute of an object, and 00115 * @li @<name@> @<value@> is a name/value argument pair. 00116 * 00117 * For example, to create a 'string' object named 'bar' with a value of 'Hello, 00118 * World!' in the 'foo' server, you might do: 00119 * 00120 * @code 00121 * $ hey foo create string with name bar value 'Hello, World!' 00122 * @endcode 00123 * 00124 * Then, you can manipulate the server and object with the following commands: 00125 * 00126 * @code 00127 * $ hey foo list 00128 * string 00129 * strings 00130 * $ hey foo list strings 00131 * bar 00132 * $ hey foo get value of string bar 00133 * Hello, World! 00134 * $ hey foo delete string bar 00135 * ok 00136 * $ hey foo list strings 00137 * $ 00138 * @endcode 00139 * 00140 * @sa HeyParserException 00141 * @sa HeyPropertyInfo 00142 * 00143 * @see cbhey.cc 00144 * @see hey_bad.cc 00145 * @see hey_good.cc 00146 */ 00147 class HeyParser 00148 { 00149 00150 public: 00151 00152 /** 00153 * An enumeration of the possible actions to perform on an object. 00154 */ 00155 /** @var HeyParser::SHUTDOWN Shutdown the server. */ 00156 typedef enum { 00157 SHUTDOWN, 00158 LIST_PROPERTIES, /**< List the properties of an object. */ 00159 GET_PROPERTY, /**< Get a property from an object. */ 00160 SET_PROPERTY, /**< Set a property in an object. */ 00161 CREATE_PROPERTY, /**< Create a property in an object. */ 00162 DELETE_PROPERTY, /**< Delete a property in an object. */ 00163 EXECUTE_PROPERTY, /**< Execute a property in an object. */ 00164 GET_SUITES, /**< Get the description of a property. */ 00165 00166 WHAT_MAX 00167 } hp_what_t; 00168 00169 /** 00170 * String versions of the hp_what_t enumeration values. 00171 */ 00172 static const char *hp_what_strings[WHAT_MAX]; 00173 00174 /** 00175 * Construct a parser that interprets the given command-line arguments. 00176 * Most of the parsing will be done here so there are fewer surprises along 00177 * the way. Note that the object expects all of the arguments, including 00178 * the command name. 00179 * 00180 * @callgraph 00181 * 00182 * @param argc The argument count. 00183 * @param argv The argument values. 00184 * 00185 * @throws HeyParserException if there is a problem with the argument list. 00186 */ 00187 HeyParser(int argc, const char *argv[]) 00188 throw (HeyParserException); 00189 00190 /** 00191 * Deconstruct a parser object. 00192 */ 00193 virtual ~HeyParser(void); 00194 00195 /** 00196 * @return The argument count. 00197 */ 00198 int getArgCount(void) const 00199 { 00200 return( this->hp_ArgCount ); 00201 } 00202 00203 /** 00204 * @return The argument values. 00205 */ 00206 const char **getArgValues(void) const 00207 { 00208 return( this->hp_ArgValue ); 00209 } 00210 00211 /** 00212 * @return The argument that specifies which server to talk to. 00213 */ 00214 const char *who(void) const 00215 { 00216 return( this->hp_ArgValue[1] ); 00217 }; 00218 00219 /** 00220 * @return The action requested by the user (i.e. what to do). 00221 */ 00222 hp_what_t what(void) const 00223 { 00224 return( this->hp_What ); 00225 }; 00226 00227 /** 00228 * Pop a pair from the property stack. 00229 * 00230 * @param name_out The reference where the property name should be stored. 00231 * @param value_out The reference where the property value should be 00232 * stored. 00233 * 00234 * @throws HeyParserException if there are no more values on the stack. 00235 */ 00236 void popProperty(const char *&name_out, const char *&value_out) 00237 throw (HeyParserException); 00238 00239 /** 00240 * @return The value that a property should be set to. 00241 */ 00242 const char *to(void) const 00243 { 00244 return( this->hp_ToValue ); 00245 }; 00246 00247 /** 00248 * Get the argument array containing the 'with' name/value pairs. 00249 * 00250 * @param with_out The reference where the 'with' array should be stored. 00251 * The elements of the array are ordered as they were on the command line. 00252 * @param count_out The size of the 'with_out' array. 00253 */ 00254 void with(const char **&with_out, size_t &count_out) const; 00255 00256 /** 00257 * Get the value for a particular 'with' argument. 00258 * 00259 * @param name The name of the 'with' argument to retrieve. 00260 * @param instance The instance of the argument to retrieve. 00261 * @param default_value The default value to return if the 'with' argument 00262 * could not be found. 00263 * @return The value coupled with the given instance of 'name', or 00264 * default_value if the instance could not be found. 00265 * 00266 * @throws HeyParserException if the particular instance of the 'with' 00267 * argument was not found. 00268 */ 00269 const char *withValue(const char *name, 00270 unsigned int instance = 0, 00271 const char *default_value = NULL) const 00272 throw (HeyParserException); 00273 00274 private: 00275 00276 /** 00277 * Helper class that stores name/value pairs. 00278 */ 00279 class Pair 00280 { 00281 00282 public: 00283 00284 /** 00285 * Construct a Pair object with the given name/value pair. 00286 * 00287 * @param name The name. 00288 * @param value The value. 00289 */ 00290 Pair(const char *name = NULL, const char *value = NULL) : 00291 p_Name(name), p_Value(value) 00292 { 00293 }; 00294 00295 /** 00296 * The name of the property. 00297 */ 00298 const char *p_Name; 00299 00300 /** 00301 * The property value. 00302 */ 00303 const char *p_Value; 00304 }; 00305 00306 /** 00307 * Consume an argument from the argument list. This method is for internal 00308 * use when initially processing the argument list. 00309 * 00310 * @sideeffect hp_ArgIndex is incremented by one if there was an argument 00311 * available. 00312 * 00313 * @return The next string in the argument list. 00314 * 00315 * @throws HeyParserException if there are no more arguments to be 00316 * consumed. 00317 */ 00318 const char *consumeArg(void) 00319 throw (HeyParserException); 00320 00321 /** 00322 * Find the next name/value pair in the argument list. This method differs 00323 * from popProperty() in that it translates from the argument list to 00324 * hp_PropertyStack. Whereas popProperty() interacts only with 00325 * hp_PropertyStack. 00326 * 00327 * @sideeffect The top element of hp_PropertyStack is popped off. 00328 * 00329 * @param name_out The reference where the property name should be stored. 00330 * @param value_out The reference where the property value should be 00331 * stored. 00332 * 00333 * @throws HeyParserException if there are insufficient arguments. 00334 */ 00335 void nextProperty(const char *&name_out, const char *&value_out) 00336 throw (HeyParserException); 00337 00338 /** 00339 * The arguments count. 00340 */ 00341 int hp_ArgCount; 00342 00343 /** 00344 * The argument values. 00345 */ 00346 const char **hp_ArgValue; 00347 00348 /** 00349 * The "what" value. This will only be set by the constructor when it is 00350 * initially parsing the arguments. 00351 */ 00352 hp_what_t hp_What; 00353 00354 /** 00355 * The 'to' value for a SET_PROPERTY request. 00356 */ 00357 const char *hp_ToValue; 00358 00359 /** 00360 * The current index in the argument list during processing. 00361 */ 00362 int hp_ArgIndex; 00363 00364 /** 00365 * The stack of property pairs. 00366 */ 00367 stack<struct Pair> hp_PropertyStack; 00368 00369 }; 00370 00371 /** 00372 * Helper class that holds descriptions of properties. 00373 * 00374 * @see HeyParser 00375 */ 00376 class HeyPropertyInfo 00377 { 00378 00379 public: 00380 00381 /** 00382 * Construct a description of a property. For example, if a server 00383 * supported the following requests on a 'string' object: 00384 * 00385 * @code 00386 * $ hey server create string with id 2 value Foobar 00387 * ok 00388 * $ hey server get string 2 00389 * Foobar 00390 * $ hey server delete string 2 00391 * ok 00392 * $ hey server get string 2 00393 * Error: No such string: 2 00394 * @endcode 00395 * 00396 * The 'suites' for these requests would be encoded by the following 00397 * array of HeyPropertyInfo's. 00398 * 00399 * @code 00400 * HeyPropertyInfo suites[] = { 00401 * HeyPropertyInfo("string", 00402 * (1L << HeyParser::CREATE_PROPERTY), 00403 * "", 00404 * "id:int - The string's identifer.\n" 00405 * "value:string - The string's value.\n" 00406 * "\n" 00407 * "Create a string with the given id and value.\n"), 00408 * HeyPropertyInfo("string", 00409 * (1L << HeyParser::GET_PROPERTY) | 00410 * (1L << HeyParser::SET_PROPERTY), 00411 * "id:int", 00412 * "Get or set the value of string 'id'.\n"), 00413 * HeyPropertyInfo("string", 00414 * (1L << HeyParser::DELETE_PROPERTY), 00415 * "id:int", 00416 * "Delete a string 'id'.\n"), 00417 * HeyPropertyInfo::HPI_NULL // TERMINATOR 00418 * }; 00419 * @endcode 00420 * 00421 * The first object indicates that the 'string' property supports 00422 * CREATE_PROPERTY and takes two arguments, the string identifier and the 00423 * string's value. The second and third objects also describe the 00424 * 'string' property, but this time, they document what happens when used 00425 * with the get, set, and delete actions. These objects are then grouped 00426 * in an array so they can be easily sent to an output stream, like so: 00427 * 00428 * @code 00429 * cout << suites; 00430 * @endcode 00431 * 00432 * The result would then look like the following: 00433 * 00434 * @code 00435 * $ hey server getsuites 00436 * Property: string 00437 * Supported verbs: create 00438 * Create a string with the given id and value. 00439 * 00440 * Property: string 00441 * Supported verbs: get set 00442 * Specifiers: id:int 00443 * Get or set the value of string 'id'. 00444 * 00445 * Property: string 00446 * Supported verbs: create 00447 * Specifiers: id:int 00448 * Delete a string 'id'. 00449 * 00450 * @endcode 00451 * 00452 * @param name The name of the property. 00453 * @param commands The commands supported by the property. The commands 00454 * are encoded as a bitmap of the HeyParser::hp_what_t values. 00455 * @param specifiers A description of the specifier argument. 00456 * @param usage A description of the property and how to use it. 00457 */ 00458 HeyPropertyInfo(const char *name, 00459 unsigned int commands, 00460 const char *specifiers = NULL, 00461 const char *usage = NULL) 00462 : hpi_Name(name), 00463 hpi_Commands(commands), 00464 hpi_Specifiers(specifiers), 00465 hpi_Usage(usage) 00466 { 00467 }; 00468 00469 /** 00470 * Deconstruct a HeyPropertyInfo. 00471 */ 00472 virtual ~HeyPropertyInfo(void) 00473 { 00474 }; 00475 00476 /** 00477 * @return The property's name. 00478 */ 00479 const char *getName(void) const 00480 { 00481 return this->hpi_Name; 00482 }; 00483 00484 /** 00485 * @return The bitmap of commands supported by this property. 00486 */ 00487 unsigned int getCommands(void) const 00488 { 00489 return this->hpi_Commands; 00490 }; 00491 00492 /** 00493 * @return The string describing the specifier argument, if any. 00494 */ 00495 const char *getSpecifiers(void) const 00496 { 00497 return this->hpi_Specifiers; 00498 }; 00499 00500 /** 00501 * @return The string describing how to use this property. 00502 */ 00503 const char *getUsage(void) const 00504 { 00505 return this->hpi_Usage; 00506 }; 00507 00508 /** 00509 * Output stream operator for HeyPropertyInfo objects. 00510 * 00511 * @param os The destination output stream. 00512 * @param hpi The object to output. 00513 * @return The value of the 'os' parameter. 00514 */ 00515 friend std::ostream &operator<<(std::ostream &os, 00516 const HeyPropertyInfo &hpi) 00517 { 00518 unsigned int lpc; 00519 00520 os << "Property: " << hpi.hpi_Name << endl 00521 << " Supported verbs:"; 00522 for( lpc = 0; lpc < HeyParser::WHAT_MAX; lpc++ ) 00523 { 00524 if( hpi.hpi_Commands & (1L << lpc) ) 00525 { 00526 os << " " << HeyParser::hp_what_strings[lpc]; 00527 } 00528 } 00529 os << endl; 00530 if( (hpi.hpi_Specifiers != NULL) && 00531 (hpi.hpi_Specifiers[0] != '\0') ) 00532 { 00533 os << " Specifiers: " << hpi.hpi_Specifiers << endl; 00534 } 00535 if( (hpi.hpi_Usage != NULL) && (hpi.hpi_Usage[0] != '\0') ) 00536 { 00537 os << hpi.hpi_Usage; 00538 } 00539 return( os ); 00540 }; 00541 00542 /** 00543 * Output stream operator for an HPI_NULL terminated array of 00544 * HeyPropertyInfo objects. 00545 * 00546 * @param os The destination output stream. 00547 * @param hpi The array to output. 00548 * @return The value of the 'os' parameter. 00549 */ 00550 friend std::ostream &operator<<(std::ostream &os, 00551 const HeyPropertyInfo hpi[]) 00552 { 00553 unsigned int lpc; 00554 00555 for( lpc = 0; hpi[lpc].getCommands() != 0; lpc++ ) 00556 { 00557 os << hpi[lpc] << endl; 00558 } 00559 return( os ); 00560 }; 00561 00562 /** 00563 * Typed NULL for HeyPropertyInfo's. 00564 */ 00565 static HeyPropertyInfo HPI_NULL; 00566 00567 private: 00568 00569 /** 00570 * The property's name. 00571 */ 00572 const char *hpi_Name; 00573 00574 /** 00575 * The bitmap of commands supported by this property. 00576 */ 00577 unsigned int hpi_Commands; 00578 00579 /** 00580 * The string describing the specifier argument, if any. 00581 */ 00582 const char *hpi_Specifiers; 00583 00584 /** 00585 * The string describing how to use this property. 00586 */ 00587 const char *hpi_Usage; 00588 00589 }; 00590 00591 #endif