00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "vgfx.h"
00036 #include "drawer.h"
00037
00038 #include <istream>
00039 #include <sstream>
00040
00041 #include "common/wave_ex.h"
00042 #include "objtree/objtree.h"
00043 #include "perf/perf.h"
00044 #include "util/parsing.h"
00045 #include "util/token_stream.h"
00046
00047
00048
00049
00050 namespace vgfx {
00051
00052 static const eParseBehavior s_line_behavior = (eParseBehavior) (
00053 eParse_StripComments |
00054 eParse_StripBogus |
00055 eParse_RespectQuotes
00056 );
00057
00058 static const eParseBehavior s_token_behavior = eParse_RespectQuotes;
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068 static bool
00069 isMultilineType
00070 (
00071 IN const char * type
00072 )
00073 throw()
00074 {
00075 ASSERT(type, "null");
00076
00077 if (!strcmp("group", type) ||
00078 !strcmp("function", type))
00079 return true;
00080 return false;
00081 }
00082
00083
00084
00085 static void
00086 breakPath
00087 (
00088 IN const char * in_path,
00089 OUT VecString& elements,
00090 OUT VecString& relpath
00091 )
00092 {
00093 ASSERT(in_path, "null");
00094
00095 elements.clear();
00096
00097 std::string current;
00098 for (const char * p = in_path; *p; ++p) {
00099 if ('.' == *p) {
00100 elements.push_back(current);
00101 current = "";
00102 continue;
00103 }
00104
00105 current += *p;
00106 }
00107 elements.push_back(current);
00108
00109 relpath.clear();
00110 for (int i = 1; i < (int) elements.size(); ++i) {
00111 relpath.push_back(elements[i]);
00112 }
00113 }
00114
00115
00116
00117 void
00118 processAdd
00119 (
00120 IN ObjectMap * map,
00121 IN const char * action
00122 )
00123 {
00124 perf::Timer timer("processAdd");
00125
00126 ASSERT(map, "null");
00127 ASSERT(action, "null");
00128
00129
00130
00131
00132 std::istringstream stream(action);
00133 std::string line, token;
00134
00135
00136 while (!stream.eof()) {
00137 line = getNextLineFromStream(stream, s_line_behavior);
00138
00139
00140 const char * cursor =
00141 getNextTokenFromString(line.c_str(), token, eParse_None);
00142
00143
00144 if ("" == token) {
00145 continue;
00146 }
00147
00148
00149 if ("group" == token) {
00150 getNextTokenFromString(cursor, token, eParse_None);
00151 if ("{" != token) {
00152 WAVE_EX(wex);
00153 wex << "Expected an opening bracket after 'group' key.";
00154 wex << " Read '" << token << "' instead?";
00155 }
00156 smart_ptr<Primitive> g = parseGroup(stream, map);
00157 ASSERT(g, "Failed to parse group?");
00158 ASSERT(!strcmp("group", g->getType()),
00159 "New object isn't a group?");
00160 map->addObject(g);
00161
00162
00163
00164 continue;
00165 }
00166
00167
00168 if ("function" == token) {
00169 getNextTokenFromString(cursor, token, eParse_None);
00170 if ("{" != token) {
00171 WAVE_EX(wex);
00172 wex << "Expected an opening bracket after 'function' key.";
00173 wex << " Read '" << token << "' instead?";
00174 }
00175 smart_ptr<Primitive> fn = parseFunction(stream, map);
00176 ASSERT(fn, "failed to parse function?");
00177 ASSERT(!strcmp("function", fn->getType()),
00178 "New object isn't a function?");
00179 map->addObject(fn);
00180 continue;
00181 }
00182
00183
00184 dictionary_t dict;
00185 getDictionaryFromString(cursor, token.c_str(), dict);
00186
00187
00188 try {
00189
00190 smart_ptr<Primitive> p =
00191 Primitive::create(token.c_str(), dict);
00192 ASSERT(p, "failed to create object of type '%s'",
00193 token.c_str());
00194
00195 map->addObject(p);
00196 } catch (std::exception& e) {
00197
00198 WAVE_EX(wex);
00199 wex << "Failed to create object of type '" << token;
00200 wex << "': " << e.what();
00201 }
00202 }
00203 }
00204
00205
00206
00207 static void
00208 processSet
00209 (
00210 IN ObjectMap * map,
00211 IN const char * action
00212 )
00213 {
00214 perf::Timer timer("processSet");
00215
00216 ASSERT(map, "null");
00217 ASSERT(action, "null");
00218
00219
00220 std::string path;
00221 const char * cursor = getNextTokenFromString(action, path, eParse_None);
00222 VecString elements, relpath;
00223 breakPath(path.c_str(), elements, relpath);
00224
00225
00226
00227
00228
00229 dictionary_t data;
00230 getDictionaryFromString(cursor, "set", data);
00231
00232
00233 Primitive * p = map->findObject(elements[0].c_str());
00234 if (!p) {
00235 WAVE_EX(wex);
00236 wex << "Set failed: object with id '" << elements[0] << "' ";
00237 wex << "does not exist";
00238 }
00239
00240
00241 p->setContainerDictionary(relpath, data);
00242 }
00243
00244
00245
00246 static void
00247 processRemove
00248 (
00249 IN ObjectMap * map,
00250 IN const char * action
00251 )
00252 {
00253 perf::Timer timer("processRemove");
00254
00255 ASSERT(map, "null");
00256 ASSERT(action, "null");
00257
00258
00259 std::string path;
00260 getNextTokenFromString(action, path, eParse_None);
00261 VecString elements, relpath;
00262 breakPath(path.c_str(), elements, relpath);
00263
00264 if (1 == elements.size()) {
00265
00266 map->removeObject(elements[0].c_str());
00267 return;
00268 }
00269
00270
00271 const char * id = elements[0].c_str();
00272 Primitive * p = map->findObject(id);
00273 if (!p) {
00274 WAVE_EX(wex);
00275 wex << "Error processing remove action: object does not exist:";
00276 wex << " '" << id << "'";
00277 }
00278
00279
00280 p->removeContainer(relpath);
00281 }
00282
00283
00284
00285 static void
00286 getActionString
00287 (
00288 IN const char * verb,
00289 IO std::istream& stream,
00290 OUT std::string& action
00291 )
00292 {
00293
00294
00295 ASSERT(verb, "null action verb");
00296
00297 action.clear();
00298
00299
00300 std::string line = getNextLineFromStream(stream, s_line_behavior);
00301 action += line;
00302
00303
00304 std::string token;
00305 getNextTokenFromString(line.c_str(), token, eParse_None);
00306 if (strcmp("add", verb) || !isMultilineType(token.c_str())) {
00307 return;
00308 }
00309
00310
00311 int depth = 1;
00312 while (depth > 0) {
00313 action += "\n";
00314 line = getNextLineFromStream(stream, s_line_behavior);
00315 action += line;
00316
00317
00318 if (strstr(line.c_str(), "{"))
00319 ++depth;
00320 if (strstr(line.c_str(), "}"))
00321 --depth;
00322 }
00323 }
00324
00325
00326
00327 static void
00328 getUndoForAction
00329 (
00330 IN ObjectMap * map,
00331 IN const char * verb,
00332 IO const char * action,
00333 OUT std::string& undo
00334 )
00335 {
00336 perf::Timer timer("getUndoForAction");
00337
00338 ASSERT(map, "null map");
00339 ASSERT(verb, "null action verb");
00340 ASSERT(action, "null action");
00341
00342 undo.clear();
00343 std::string line, path, token;
00344
00345 if (!strcmp("add", verb)) {
00346
00347 undo = "remove ";
00348 const char * cursor =
00349 getNextTokenFromString(action, token, eParse_None);
00350 if (!isMultilineType(token.c_str())) {
00351
00352 dictionary_t data;
00353 getDictionaryFromString(cursor, token.c_str(), data);
00354 undo += getRequiredValue(data, "id");
00355 } else {
00356
00357
00358 std::istringstream iss(action);
00359 const char * cursor = NULL;
00360 while ("id" != token) {
00361 line =
00362 getNextLineFromStream(iss, s_line_behavior);
00363 cursor = getNextTokenFromString(line.c_str(),
00364 token, eParse_None);
00365 }
00366 getNextTokenFromString(cursor, token, eParse_None);
00367 undo += token;
00368 }
00369 } else if (!strcmp("remove", verb)) {
00370
00371
00372
00373 getNextTokenFromString(action, path, eParse_None);
00374 VecString elements, relpath;
00375 breakPath(path.c_str(), elements, relpath);
00376
00377
00378 Primitive * p = map->findObject(elements[0].c_str());
00379 if (!p) {
00380 WAVE_EX(wex);
00381 wex << "Asking to remove an object that does not ";
00382 wex << "exist: '" << elements[0] << "'";
00383 }
00384
00385 if (1 == elements.size()) {
00386
00387
00388 undo = "add ";
00389 std::ostringstream persist;
00390 p->persist(persist);
00391 undo += persist.str();
00392 } else {
00393
00394 undo = "set ";
00395 undo += path;
00396 undo += " ";
00397
00398
00399 dictionary_t data;
00400 p->getContainerDictionary(relpath, data);
00401 for (dictionary_t::iterator i = data.begin();
00402 i != data.end(); ++i) {
00403 undo += i->first;
00404 undo += " \"";
00405 undo += i->second;
00406 undo += "\" ";
00407 }
00408 }
00409 } else if (!strcmp("set", verb)) {
00410
00411
00412
00413
00414
00415 const char * cursor =
00416 getNextTokenFromString(action, path, eParse_None);
00417 VecString elements, relpath;
00418 breakPath(path.c_str(), elements, relpath);
00419 if (elements.size() < 1) {
00420 WAVE_EX(wex);
00421 wex << "Invalid path: '" << path << "'";
00422 }
00423
00424
00425 const char * id = elements[0].c_str();
00426 Primitive * p = map->findObject(id);
00427 if (!p) {
00428 WAVE_EX(wex);
00429 wex << "Attempting to set attribute value for object ";
00430 wex << "that does not exist, id='" << id << "'";
00431 }
00432
00433
00434 if (p->doesContainerExist(relpath)) {
00435
00436 undo = "set ";
00437 undo += path;
00438
00439
00440
00441 dictionary_t new_values;
00442 getDictionaryFromString(cursor, "set", new_values);
00443
00444 dictionary_t old_values;
00445 p->getContainerDictionary(relpath, old_values);
00446
00447 for (dictionary_t::iterator i = new_values.begin();
00448 i != new_values.end(); ++i) {
00449
00450 const char * name = i->first.c_str();
00451
00452 dictionary_t::iterator d = old_values.find(name);
00453 if (old_values.end() == d) {
00454 WAVE_EX(wex);
00455 wex << "Trying to set an attribute '";
00456 wex << name << "' that does not exist";
00457 wex << "in its container '" << path;
00458 wex << "'";
00459 }
00460
00461 undo += " ";
00462 undo += name;
00463 undo += " \"";
00464 undo += d->second;
00465 undo += "\"";
00466 }
00467 } else {
00468
00469 if (!p->canCreateContainer(relpath)) {
00470 WAVE_EX(wex);
00471 wex << "Asking to set values in a container ";
00472 wex << "that does not exist: '" << relpath[0];
00473 wex << "'";
00474 }
00475
00476
00477 undo = "remove ";
00478 undo += path;
00479 }
00480 } else {
00481 WAVE_EX(wex);
00482 wex << "Invalid action verb: '" << verb << "', cannot undo.";
00483 }
00484 }
00485
00486
00487
00488 static void
00489 processAction
00490 (
00491 IN ObjectMap * map,
00492 IN const char * verb,
00493 IN const char * action
00494 )
00495 {
00496 perf::Timer timer("processAction");
00497
00498 ASSERT(map, "null");
00499 ASSERT(verb, "null");
00500 ASSERT(action, "null");
00501
00502
00503 if (!strcmp("add", verb)) {
00504
00505 processAdd(map, action);
00506 } else if (!strcmp("remove", verb)) {
00507
00508 processRemove(map, action);
00509 } else if (!strcmp("set", verb)) {
00510
00511 processSet(map, action);
00512 } else {
00513 WAVE_EX(wex);
00514 wex << "Cannot process this action verb: '" << verb << "'";
00515 }
00516 }
00517
00518
00519
00520 static void
00521 processRequestInternal
00522 (
00523 IN ObjectMap * map,
00524 IN std::istream& stream,
00525 IN bool single_transaction,
00526 OUT VecString& undo_actions
00527 )
00528 {
00529 perf::Timer timer("processRequestInternal");
00530
00531
00532
00533
00534
00535 ASSERT(map, "null");
00536 ASSERT(stream.good(), "bad request stream");
00537
00538 undo_actions.clear();
00539
00540
00541 expectToken(stream, "request");
00542 expectToken(stream, "{");
00543
00544
00545 std::string token;
00546 while (true) {
00547 getNextToken(stream, token);
00548 if (token == "}") {
00549 break;
00550 }
00551 const char * verb = token.c_str();
00552
00553
00554
00555 if (token != "add" &&
00556 token != "remove" &&
00557 token != "set") {
00558 WAVE_EX(wex);
00559 wex << "Invalid action verb: '" << verb << "'";
00560 }
00561
00562
00563
00564 std::string action_str;
00565 getActionString(verb, stream, action_str);
00566 const char * action = action_str.c_str();
00567
00568
00569
00570
00571 std::string undo;
00572 if (single_transaction) {
00573 try {
00574 getUndoForAction(map, verb, action, undo);
00575 } catch (std::exception& e) {
00576 WAVE_EX(wex);
00577 wex << "Error calculating undo action for this";
00578 wex << " action:\n" << verb << " " << action;
00579 wex << "\nError: " << e.what();
00580 }
00581
00582 }
00583
00584
00585
00586 try {
00587 processAction(map, verb, action);
00588 } catch (std::exception& e) {
00589 WAVE_EX(wex);
00590 wex << "Error processing this action:\n";
00591 wex << verb << " " << action << "\n";
00592 wex << "Error: " << e.what();
00593 }
00594
00595
00596 if (single_transaction) {
00597 undo_actions.push_back(undo);
00598 }
00599 }
00600 }
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610 bool
00611 processRequest
00612 (
00613 IN ObjectMap * map,
00614 IN std::istream& stream,
00615 IN bool single_transaction,
00616 OUT std::string& undo_request,
00617 OUT std::string& diagnostic
00618 )
00619 {
00620 perf::Timer timer("processRequest");
00621
00622 ASSERT(map, "null map in processRequest()");
00623 ASSERT(stream.good(), "bad stream in processRequest()");
00624
00625
00626 diagnostic.clear();
00627 undo_request.clear();
00628
00629 VecString undo_actions;
00630
00631
00632 bool success = false;
00633 try {
00634 processRequestInternal(map, stream, single_transaction,
00635 undo_actions);
00636 success = true;
00637 } catch (std::exception& e) {
00638 diagnostic = e.what();
00639 } catch (...) {
00640 diagnostic = "Unknown exception! Hard failure.";
00641 WAVE_EX(wex);
00642 wex << diagnostic;
00643 }
00644
00645
00646 if (single_transaction) {
00647
00648 undo_request = "request {\n";
00649 for (VecString::reverse_iterator iter = undo_actions.rbegin();
00650 iter != undo_actions.rend(); ++iter) {
00651 undo_request += *iter;
00652 undo_request += "\n";
00653 }
00654 undo_request += "}";
00655 }
00656
00657
00658
00659 if (success) {
00660 diagnostic = "Success.";
00661 } else if (single_transaction) {
00662
00663
00664 std::string local_undo = undo_request;
00665 undo_request = "";
00666 try {
00667 VecString unused_undo;
00668 std::istringstream undo_stream(local_undo);
00669 processRequestInternal(map, undo_stream, false,
00670 unused_undo);
00671 } catch (std::exception& e) {
00672 diagnostic += "\nEncountered exception while trying to";
00673 diagnostic += " roll back: ";
00674 diagnostic += e.what();
00675 diagnostic += "\nHard failure.";
00676 WAVE_EX(wex);
00677 wex << diagnostic;
00678 } catch (...) {
00679 diagnostic += "\nUnknown exception while attempting to";
00680 diagnostic += " roll back!\nHard failure.";
00681 WAVE_EX(wex);
00682 wex << diagnostic;
00683 }
00684 }
00685
00686
00687 return success;
00688 }
00689
00690
00691
00692 };
00693