Properties.cpp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270
  1. #include "Base.h"
  2. #include "Properties.h"
  3. #include "FileSystem.h"
  4. #include "Quaternion.h"
  5. namespace gameplay
  6. {
  7. /**
  8. * Reads the next character from the stream. Returns EOF if the end of the stream is reached.
  9. */
  10. static signed char readChar(Stream* stream)
  11. {
  12. if (stream->eof())
  13. return EOF;
  14. signed char c;
  15. if (stream->read(&c, 1, 1) != 1)
  16. return EOF;
  17. return c;
  18. }
  19. // Utility functions (shared with SceneLoader).
  20. /** @script{ignore} */
  21. void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
  22. /** @script{ignore} */
  23. Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
  24. Properties::Properties()
  25. : _variables(NULL), _dirPath(NULL), _parent(NULL)
  26. {
  27. }
  28. Properties::Properties(const Properties& copy)
  29. : _namespace(copy._namespace), _id(copy._id), _parentID(copy._parentID), _properties(copy._properties), _variables(NULL), _dirPath(NULL), _parent(copy._parent)
  30. {
  31. setDirectoryPath(copy._dirPath);
  32. _namespaces = std::vector<Properties*>();
  33. std::vector<Properties*>::const_iterator it;
  34. for (it = copy._namespaces.begin(); it < copy._namespaces.end(); ++it)
  35. {
  36. GP_ASSERT(*it);
  37. _namespaces.push_back(new Properties(**it));
  38. }
  39. rewind();
  40. }
  41. Properties::Properties(Stream* stream)
  42. : _variables(NULL), _dirPath(NULL), _parent(NULL)
  43. {
  44. readProperties(stream);
  45. rewind();
  46. }
  47. Properties::Properties(Stream* stream, const char* name, const char* id, const char* parentID, Properties* parent)
  48. : _namespace(name), _variables(NULL), _dirPath(NULL), _parent(parent)
  49. {
  50. if (id)
  51. {
  52. _id = id;
  53. }
  54. if (parentID)
  55. {
  56. _parentID = parentID;
  57. }
  58. readProperties(stream);
  59. rewind();
  60. }
  61. Properties* Properties::create(const char* url)
  62. {
  63. if (!url || strlen(url) == 0)
  64. {
  65. GP_ERROR("Attempting to create a Properties object from an empty URL!");
  66. return NULL;
  67. }
  68. // Calculate the file and full namespace path from the specified url.
  69. std::string urlString = url;
  70. std::string fileString;
  71. std::vector<std::string> namespacePath;
  72. calculateNamespacePath(urlString, fileString, namespacePath);
  73. std::unique_ptr<Stream> stream(FileSystem::open(fileString.c_str()));
  74. if (stream.get() == NULL)
  75. {
  76. GP_WARN("Failed to open file '%s'.", fileString.c_str());
  77. return NULL;
  78. }
  79. Properties* properties = new Properties(stream.get());
  80. properties->resolveInheritance();
  81. stream->close();
  82. // Get the specified properties object.
  83. Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
  84. if (!p)
  85. {
  86. GP_WARN("Failed to load properties from url '%s'.", url);
  87. SAFE_DELETE(properties);
  88. return NULL;
  89. }
  90. // If the loaded properties object is not the root namespace,
  91. // then we have to clone it and delete the root namespace
  92. // so that we don't leak memory.
  93. if (p != properties)
  94. {
  95. p = p->clone();
  96. SAFE_DELETE(properties);
  97. }
  98. p->setDirectoryPath(FileSystem::getDirectoryName(fileString.c_str()));
  99. return p;
  100. }
  101. static bool isVariable(const char* str, char* outName, size_t outSize)
  102. {
  103. size_t len = strlen(str);
  104. if (len > 3 && str[0] == '$' && str[1] == '{' && str[len - 1] == '}')
  105. {
  106. size_t size = len - 3;
  107. if (size > (outSize - 1))
  108. size = outSize - 1;
  109. strncpy(outName, str + 2, len - 3);
  110. outName[len - 3] = 0;
  111. return true;
  112. }
  113. return false;
  114. }
  115. void Properties::readProperties(Stream* stream)
  116. {
  117. GP_ASSERT(stream);
  118. char line[2048];
  119. char variable[256];
  120. int c;
  121. char* name;
  122. char* value;
  123. char* parentID;
  124. char* rc;
  125. char* rcc;
  126. char* rccc;
  127. bool comment = false;
  128. while (true)
  129. {
  130. // Skip whitespace at the start of lines
  131. skipWhiteSpace(stream);
  132. // Stop when we have reached the end of the file.
  133. if (stream->eof())
  134. break;
  135. // Read the next line.
  136. rc = stream->readLine(line, 2048);
  137. if (rc == NULL)
  138. {
  139. GP_ERROR("Error reading line from file.");
  140. return;
  141. }
  142. // Ignore comments
  143. if (comment)
  144. {
  145. // Check for end of multi-line comment at either start or end of line
  146. if (strncmp(line, "*/", 2) == 0)
  147. comment = false;
  148. else
  149. {
  150. trimWhiteSpace(line);
  151. const int len = strlen(line);
  152. if (len >= 2 && strncmp(line + (len - 2), "*/", 2) == 0)
  153. comment = false;
  154. }
  155. }
  156. else if (strncmp(line, "/*", 2) == 0)
  157. {
  158. // Start of multi-line comment (must be at start of line)
  159. comment = true;
  160. }
  161. else if (strncmp(line, "//", 2) != 0)
  162. {
  163. // If an '=' appears on this line, parse it as a name/value pair.
  164. // Note: strchr() has to be called before strtok(), or a backup of line has to be kept.
  165. rc = strchr(line, '=');
  166. if (rc != NULL)
  167. {
  168. // First token should be the property name.
  169. name = strtok(line, "=");
  170. if (name == NULL)
  171. {
  172. GP_ERROR("Error parsing properties file: attribute without name.");
  173. return;
  174. }
  175. // Remove white-space from name.
  176. name = trimWhiteSpace(name);
  177. // Scan for next token, the property's value.
  178. value = strtok(NULL, "");
  179. if (value == NULL)
  180. {
  181. GP_ERROR("Error parsing properties file: attribute with name ('%s') but no value.", name);
  182. return;
  183. }
  184. // Remove white-space from value.
  185. value = trimWhiteSpace(value);
  186. // Is this a variable assignment?
  187. if (isVariable(name, variable, 256))
  188. {
  189. setVariable(variable, value);
  190. }
  191. else
  192. {
  193. // Normal name/value pair
  194. _properties.push_back(Property(name, value));
  195. }
  196. }
  197. else
  198. {
  199. parentID = NULL;
  200. // Get the last character on the line (ignoring whitespace).
  201. const char* lineEnd = trimWhiteSpace(line) + (strlen(trimWhiteSpace(line)) - 1);
  202. // This line might begin or end a namespace,
  203. // or it might be a key/value pair without '='.
  204. // Check for '{' on same line.
  205. rc = strchr(line, '{');
  206. // Check for inheritance: ':'
  207. rcc = strchr(line, ':');
  208. // Check for '}' on same line.
  209. rccc = strchr(line, '}');
  210. // Get the name of the namespace.
  211. name = strtok(line, " \t\n{");
  212. name = trimWhiteSpace(name);
  213. if (name == NULL)
  214. {
  215. GP_ERROR("Error parsing properties file: failed to determine a valid token for line '%s'.", line);
  216. return;
  217. }
  218. else if (name[0] == '}')
  219. {
  220. // End of namespace.
  221. return;
  222. }
  223. // Get its ID if it has one.
  224. value = strtok(NULL, ":{");
  225. value = trimWhiteSpace(value);
  226. // Get its parent ID if it has one.
  227. if (rcc != NULL)
  228. {
  229. parentID = strtok(NULL, "{");
  230. parentID = trimWhiteSpace(parentID);
  231. }
  232. if (value != NULL && value[0] == '{')
  233. {
  234. // If the namespace ends on this line, seek back to right before the '}' character.
  235. if (rccc && rccc == lineEnd)
  236. {
  237. if (stream->seek(-1, SEEK_CUR) == false)
  238. {
  239. GP_ERROR("Failed to seek back to before a '}' character in properties file.");
  240. return;
  241. }
  242. while (readChar(stream) != '}')
  243. {
  244. if (stream->seek(-2, SEEK_CUR) == false)
  245. {
  246. GP_ERROR("Failed to seek back to before a '}' character in properties file.");
  247. return;
  248. }
  249. }
  250. if (stream->seek(-1, SEEK_CUR) == false)
  251. {
  252. GP_ERROR("Failed to seek back to before a '}' character in properties file.");
  253. return;
  254. }
  255. }
  256. // New namespace without an ID.
  257. Properties* space = new Properties(stream, name, NULL, parentID, this);
  258. _namespaces.push_back(space);
  259. // If the namespace ends on this line, seek to right after the '}' character.
  260. if (rccc && rccc == lineEnd)
  261. {
  262. if (stream->seek(1, SEEK_CUR) == false)
  263. {
  264. GP_ERROR("Failed to seek to immediately after a '}' character in properties file.");
  265. return;
  266. }
  267. }
  268. }
  269. else
  270. {
  271. // If '{' appears on the same line.
  272. if (rc != NULL)
  273. {
  274. // If the namespace ends on this line, seek back to right before the '}' character.
  275. if (rccc && rccc == lineEnd)
  276. {
  277. if (stream->seek(-1, SEEK_CUR) == false)
  278. {
  279. GP_ERROR("Failed to seek back to before a '}' character in properties file.");
  280. return;
  281. }
  282. while (readChar(stream) != '}')
  283. {
  284. if (stream->seek(-2, SEEK_CUR) == false)
  285. {
  286. GP_ERROR("Failed to seek back to before a '}' character in properties file.");
  287. return;
  288. }
  289. }
  290. if (stream->seek(-1, SEEK_CUR) == false)
  291. {
  292. GP_ERROR("Failed to seek back to before a '}' character in properties file.");
  293. return;
  294. }
  295. }
  296. // Create new namespace.
  297. Properties* space = new Properties(stream, name, value, parentID, this);
  298. _namespaces.push_back(space);
  299. // If the namespace ends on this line, seek to right after the '}' character.
  300. if (rccc && rccc == lineEnd)
  301. {
  302. if (stream->seek(1, SEEK_CUR) == false)
  303. {
  304. GP_ERROR("Failed to seek to immediately after a '}' character in properties file.");
  305. return;
  306. }
  307. }
  308. }
  309. else
  310. {
  311. // Find out if the next line starts with "{"
  312. skipWhiteSpace(stream);
  313. c = readChar(stream);
  314. if (c == '{')
  315. {
  316. // Create new namespace.
  317. Properties* space = new Properties(stream, name, value, parentID, this);
  318. _namespaces.push_back(space);
  319. }
  320. else
  321. {
  322. // Back up from fgetc()
  323. if (stream->seek(-1, SEEK_CUR) == false)
  324. GP_ERROR("Failed to seek backwards a single character after testing if the next line starts with '{'.");
  325. // Store "name value" as a name/value pair, or even just "name".
  326. if (value != NULL)
  327. {
  328. _properties.push_back(Property(name, value));
  329. }
  330. else
  331. {
  332. _properties.push_back(Property(name, ""));
  333. }
  334. }
  335. }
  336. }
  337. }
  338. }
  339. }
  340. }
  341. Properties::~Properties()
  342. {
  343. SAFE_DELETE(_dirPath);
  344. for (size_t i = 0, count = _namespaces.size(); i < count; ++i)
  345. {
  346. SAFE_DELETE(_namespaces[i]);
  347. }
  348. SAFE_DELETE(_variables);
  349. }
  350. void Properties::skipWhiteSpace(Stream* stream)
  351. {
  352. signed char c;
  353. do
  354. {
  355. c = readChar(stream);
  356. } while (isspace(c) && c != EOF);
  357. // If we are not at the end of the file, then since we found a
  358. // non-whitespace character, we put the cursor back in front of it.
  359. if (c != EOF)
  360. {
  361. if (stream->seek(-1, SEEK_CUR) == false)
  362. {
  363. GP_ERROR("Failed to seek backwards one character after skipping whitespace.");
  364. }
  365. }
  366. }
  367. char* Properties::trimWhiteSpace(char *str)
  368. {
  369. if (str == NULL)
  370. {
  371. return str;
  372. }
  373. char *end;
  374. // Trim leading space.
  375. while (isspace(*str))
  376. str++;
  377. // All spaces?
  378. if (*str == 0)
  379. {
  380. return str;
  381. }
  382. // Trim trailing space.
  383. end = str + strlen(str) - 1;
  384. while (end > str && isspace(*end))
  385. end--;
  386. // Write new null terminator.
  387. *(end+1) = 0;
  388. return str;
  389. }
  390. void Properties::resolveInheritance(const char* id)
  391. {
  392. // Namespaces can be defined like so:
  393. // "name id : parentID { }"
  394. // This method merges data from the parent namespace into the child.
  395. // Get a top-level namespace.
  396. Properties* derived;
  397. if (id)
  398. {
  399. derived = getNamespace(id);
  400. }
  401. else
  402. {
  403. derived = getNextNamespace();
  404. }
  405. while (derived)
  406. {
  407. // If the namespace has a parent ID, find the parent.
  408. if (!derived->_parentID.empty())
  409. {
  410. Properties* parent = getNamespace(derived->_parentID.c_str());
  411. if (parent)
  412. {
  413. resolveInheritance(parent->getId());
  414. // Copy the child.
  415. Properties* overrides = new Properties(*derived);
  416. // Delete the child's data.
  417. for (size_t i = 0, count = derived->_namespaces.size(); i < count; i++)
  418. {
  419. SAFE_DELETE(derived->_namespaces[i]);
  420. }
  421. // Copy data from the parent into the child.
  422. derived->_properties = parent->_properties;
  423. derived->_namespaces = std::vector<Properties*>();
  424. std::vector<Properties*>::const_iterator itt;
  425. for (itt = parent->_namespaces.begin(); itt < parent->_namespaces.end(); ++itt)
  426. {
  427. GP_ASSERT(*itt);
  428. derived->_namespaces.push_back(new Properties(**itt));
  429. }
  430. derived->rewind();
  431. // Take the original copy of the child and override the data copied from the parent.
  432. derived->mergeWith(overrides);
  433. // Delete the child copy.
  434. SAFE_DELETE(overrides);
  435. }
  436. }
  437. // Resolve inheritance within this namespace.
  438. derived->resolveInheritance();
  439. // Get the next top-level namespace and check again.
  440. if (!id)
  441. {
  442. derived = getNextNamespace();
  443. }
  444. else
  445. {
  446. derived = NULL;
  447. }
  448. }
  449. }
  450. void Properties::mergeWith(Properties* overrides)
  451. {
  452. GP_ASSERT(overrides);
  453. // Overwrite or add each property found in child.
  454. overrides->rewind();
  455. const char* name = overrides->getNextProperty();
  456. while (name)
  457. {
  458. this->setString(name, overrides->getString());
  459. name = overrides->getNextProperty();
  460. }
  461. this->_propertiesItr = this->_properties.end();
  462. // Merge all common nested namespaces, add new ones.
  463. Properties* overridesNamespace = overrides->getNextNamespace();
  464. while (overridesNamespace)
  465. {
  466. bool merged = false;
  467. rewind();
  468. Properties* derivedNamespace = getNextNamespace();
  469. while (derivedNamespace)
  470. {
  471. if (strcmp(derivedNamespace->getNamespace(), overridesNamespace->getNamespace()) == 0 &&
  472. strcmp(derivedNamespace->getId(), overridesNamespace->getId()) == 0)
  473. {
  474. derivedNamespace->mergeWith(overridesNamespace);
  475. merged = true;
  476. }
  477. derivedNamespace = getNextNamespace();
  478. }
  479. if (!merged)
  480. {
  481. // Add this new namespace.
  482. Properties* newNamespace = new Properties(*overridesNamespace);
  483. this->_namespaces.push_back(newNamespace);
  484. this->_namespacesItr = this->_namespaces.end();
  485. }
  486. overridesNamespace = overrides->getNextNamespace();
  487. }
  488. }
  489. const char* Properties::getNextProperty()
  490. {
  491. if (_propertiesItr == _properties.end())
  492. {
  493. // Restart from the beginning
  494. _propertiesItr = _properties.begin();
  495. }
  496. else
  497. {
  498. // Move to the next property
  499. ++_propertiesItr;
  500. }
  501. return _propertiesItr == _properties.end() ? NULL : _propertiesItr->name.c_str();
  502. }
  503. Properties* Properties::getNextNamespace()
  504. {
  505. if (_namespacesItr == _namespaces.end())
  506. {
  507. // Restart from the beginning
  508. _namespacesItr = _namespaces.begin();
  509. }
  510. else
  511. {
  512. ++_namespacesItr;
  513. }
  514. if (_namespacesItr != _namespaces.end())
  515. {
  516. Properties* ns = *_namespacesItr;
  517. return ns;
  518. }
  519. return NULL;
  520. }
  521. void Properties::rewind()
  522. {
  523. _propertiesItr = _properties.end();
  524. _namespacesItr = _namespaces.end();
  525. }
  526. Properties* Properties::getNamespace(const char* id, bool searchNames, bool recurse) const
  527. {
  528. GP_ASSERT(id);
  529. for (std::vector<Properties*>::const_iterator it = _namespaces.begin(); it < _namespaces.end(); ++it)
  530. {
  531. Properties* p = *it;
  532. if (strcmp(searchNames ? p->_namespace.c_str() : p->_id.c_str(), id) == 0)
  533. return p;
  534. if (recurse)
  535. {
  536. // Search recursively.
  537. p = p->getNamespace(id, searchNames, true);
  538. if (p)
  539. return p;
  540. }
  541. }
  542. return NULL;
  543. }
  544. const char* Properties::getNamespace() const
  545. {
  546. return _namespace.c_str();
  547. }
  548. const char* Properties::getId() const
  549. {
  550. return _id.c_str();
  551. }
  552. bool Properties::exists(const char* name) const
  553. {
  554. if (name == NULL)
  555. return false;
  556. for (std::list<Property>::const_iterator itr = _properties.begin(); itr != _properties.end(); ++itr)
  557. {
  558. if (itr->name == name)
  559. return true;
  560. }
  561. return false;
  562. }
  563. static const bool isStringNumeric(const char* str)
  564. {
  565. GP_ASSERT(str);
  566. // The first character may be '-'
  567. if (*str == '-')
  568. str++;
  569. // The first character after the sign must be a digit
  570. if (!isdigit(*str))
  571. return false;
  572. str++;
  573. // All remaining characters must be digits, with a single decimal (.) permitted
  574. unsigned int decimalCount = 0;
  575. while (*str)
  576. {
  577. if (!isdigit(*str))
  578. {
  579. if (*str == '.' && decimalCount == 0)
  580. {
  581. // Max of 1 decimal allowed
  582. decimalCount++;
  583. }
  584. else
  585. {
  586. return false;
  587. }
  588. }
  589. str++;
  590. }
  591. return true;
  592. }
  593. Properties::Type Properties::getType(const char* name) const
  594. {
  595. const char* value = getString(name);
  596. if (!value)
  597. {
  598. return Properties::NONE;
  599. }
  600. // Parse the value to determine the format
  601. unsigned int commaCount = 0;
  602. char* valuePtr = const_cast<char*>(value);
  603. while ((valuePtr = strchr(valuePtr, ',')))
  604. {
  605. valuePtr++;
  606. commaCount++;
  607. }
  608. switch (commaCount)
  609. {
  610. case 0:
  611. return isStringNumeric(value) ? Properties::NUMBER : Properties::STRING;
  612. case 1:
  613. return Properties::VECTOR2;
  614. case 2:
  615. return Properties::VECTOR3;
  616. case 3:
  617. return Properties::VECTOR4;
  618. case 15:
  619. return Properties::MATRIX;
  620. default:
  621. return Properties::STRING;
  622. }
  623. }
  624. const char* Properties::getString(const char* name, const char* defaultValue) const
  625. {
  626. char variable[256];
  627. const char* value = NULL;
  628. if (name)
  629. {
  630. // If 'name' is a variable, return the variable value
  631. if (isVariable(name, variable, 256))
  632. {
  633. return getVariable(variable, defaultValue);
  634. }
  635. for (std::list<Property>::const_iterator itr = _properties.begin(); itr != _properties.end(); ++itr)
  636. {
  637. if (itr->name == name)
  638. {
  639. value = itr->value.c_str();
  640. break;
  641. }
  642. }
  643. }
  644. else
  645. {
  646. // No name provided - get the value at the current iterator position
  647. if (_propertiesItr != _properties.end())
  648. {
  649. value = _propertiesItr->value.c_str();
  650. }
  651. }
  652. if (value)
  653. {
  654. // If the value references a variable, return the variable value
  655. if (isVariable(value, variable, 256))
  656. return getVariable(variable, defaultValue);
  657. return value;
  658. }
  659. return defaultValue;
  660. }
  661. bool Properties::setString(const char* name, const char* value)
  662. {
  663. if (name)
  664. {
  665. for (std::list<Property>::iterator itr = _properties.begin(); itr != _properties.end(); ++itr)
  666. {
  667. if (itr->name == name)
  668. {
  669. // Update the first property that matches this name
  670. itr->value = value ? value : "";
  671. return true;
  672. }
  673. }
  674. // There is no property with this name, so add one
  675. _properties.push_back(Property(name, value ? value : ""));
  676. }
  677. else
  678. {
  679. // If there's a current property, set its value
  680. if (_propertiesItr == _properties.end())
  681. return false;
  682. _propertiesItr->value = value ? value : "";
  683. }
  684. return true;
  685. }
  686. bool Properties::getBool(const char* name, bool defaultValue) const
  687. {
  688. const char* valueString = getString(name);
  689. if (valueString)
  690. {
  691. return (strcmp(valueString, "true") == 0);
  692. }
  693. return defaultValue;
  694. }
  695. int Properties::getInt(const char* name) const
  696. {
  697. const char* valueString = getString(name);
  698. if (valueString)
  699. {
  700. int value;
  701. int scanned;
  702. scanned = sscanf(valueString, "%d", &value);
  703. if (scanned != 1)
  704. {
  705. GP_ERROR("Error attempting to parse property '%s' as an integer.", name);
  706. return 0;
  707. }
  708. return value;
  709. }
  710. return 0;
  711. }
  712. float Properties::getFloat(const char* name) const
  713. {
  714. const char* valueString = getString(name);
  715. if (valueString)
  716. {
  717. float value;
  718. int scanned;
  719. scanned = sscanf(valueString, "%f", &value);
  720. if (scanned != 1)
  721. {
  722. GP_ERROR("Error attempting to parse property '%s' as a float.", name);
  723. return 0.0f;
  724. }
  725. return value;
  726. }
  727. return 0.0f;
  728. }
  729. long Properties::getLong(const char* name) const
  730. {
  731. const char* valueString = getString(name);
  732. if (valueString)
  733. {
  734. long value;
  735. int scanned;
  736. scanned = sscanf(valueString, "%ld", &value);
  737. if (scanned != 1)
  738. {
  739. GP_ERROR("Error attempting to parse property '%s' as a long integer.", name);
  740. return 0L;
  741. }
  742. return value;
  743. }
  744. return 0L;
  745. }
  746. bool Properties::getMatrix(const char* name, Matrix* out) const
  747. {
  748. GP_ASSERT(out);
  749. const char* valueString = getString(name);
  750. if (valueString)
  751. {
  752. float m[16];
  753. int scanned;
  754. scanned = sscanf(valueString, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f",
  755. &m[0], &m[1], &m[2], &m[3], &m[4], &m[5], &m[6], &m[7],
  756. &m[8], &m[9], &m[10], &m[11], &m[12], &m[13], &m[14], &m[15]);
  757. if (scanned != 16)
  758. {
  759. GP_ERROR("Error attempting to parse property '%s' as a matrix.", name);
  760. out->setIdentity();
  761. return false;
  762. }
  763. out->set(m);
  764. return true;
  765. }
  766. out->setIdentity();
  767. return false;
  768. }
  769. bool Properties::getVector2(const char* name, Vector2* out) const
  770. {
  771. return parseVector2(getString(name), out);
  772. }
  773. bool Properties::getVector3(const char* name, Vector3* out) const
  774. {
  775. return parseVector3(getString(name), out);
  776. }
  777. bool Properties::getVector4(const char* name, Vector4* out) const
  778. {
  779. return parseVector4(getString(name), out);
  780. }
  781. bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) const
  782. {
  783. return parseAxisAngle(getString(name), out);
  784. }
  785. bool Properties::getColor(const char* name, Vector3* out) const
  786. {
  787. return parseColor(getString(name), out);
  788. }
  789. bool Properties::getColor(const char* name, Vector4* out) const
  790. {
  791. return parseColor(getString(name), out);
  792. }
  793. bool Properties::getPath(const char* name, std::string* path) const
  794. {
  795. GP_ASSERT(name && path);
  796. const char* valueString = getString(name);
  797. if (valueString)
  798. {
  799. if (FileSystem::fileExists(valueString))
  800. {
  801. path->assign(valueString);
  802. return true;
  803. }
  804. else
  805. {
  806. const Properties* prop = this;
  807. while (prop != NULL)
  808. {
  809. // Search for the file path relative to the bundle file
  810. const std::string* dirPath = prop->_dirPath;
  811. if (dirPath != NULL && !dirPath->empty())
  812. {
  813. std::string relativePath = *dirPath;
  814. relativePath.append(valueString);
  815. if (FileSystem::fileExists(relativePath.c_str()))
  816. {
  817. path->assign(relativePath);
  818. return true;
  819. }
  820. }
  821. prop = prop->_parent;
  822. }
  823. }
  824. }
  825. return false;
  826. }
  827. const char* Properties::getVariable(const char* name, const char* defaultValue) const
  828. {
  829. if (name == NULL)
  830. return defaultValue;
  831. // Search for variable in this Properties object
  832. if (_variables)
  833. {
  834. for (size_t i = 0, count = _variables->size(); i < count; ++i)
  835. {
  836. Property& prop = (*_variables)[i];
  837. if (prop.name == name)
  838. return prop.value.c_str();
  839. }
  840. }
  841. // Search for variable in parent Properties
  842. return _parent ? _parent->getVariable(name, defaultValue) : defaultValue;
  843. }
  844. void Properties::setVariable(const char* name, const char* value)
  845. {
  846. GP_ASSERT(name);
  847. Property* prop = NULL;
  848. // Search for variable in this Properties object and parents
  849. Properties* current = const_cast<Properties*>(this);
  850. while (current)
  851. {
  852. if (current->_variables)
  853. {
  854. for (size_t i = 0, count = current->_variables->size(); i < count; ++i)
  855. {
  856. Property* p = &(*current->_variables)[i];
  857. if (p->name == name)
  858. {
  859. prop = p;
  860. break;
  861. }
  862. }
  863. }
  864. current = current->_parent;
  865. }
  866. if (prop)
  867. {
  868. // Found an existing property, set it
  869. prop->value = value ? value : "";
  870. }
  871. else
  872. {
  873. // Add a new variable with this name
  874. if (!_variables)
  875. _variables = new std::vector<Property>();
  876. _variables->push_back(Property(name, value ? value : ""));
  877. }
  878. }
  879. Properties* Properties::clone()
  880. {
  881. Properties* p = new Properties();
  882. p->_namespace = _namespace;
  883. p->_id = _id;
  884. p->_parentID = _parentID;
  885. p->_properties = _properties;
  886. p->_propertiesItr = p->_properties.end();
  887. p->setDirectoryPath(_dirPath);
  888. for (size_t i = 0, count = _namespaces.size(); i < count; i++)
  889. {
  890. GP_ASSERT(_namespaces[i]);
  891. Properties* child = _namespaces[i]->clone();
  892. p->_namespaces.push_back(child);
  893. child->_parent = p;
  894. }
  895. p->_namespacesItr = p->_namespaces.end();
  896. return p;
  897. }
  898. void Properties::setDirectoryPath(const std::string* path)
  899. {
  900. if (path)
  901. {
  902. setDirectoryPath(*path);
  903. }
  904. else
  905. {
  906. SAFE_DELETE(_dirPath);
  907. }
  908. }
  909. void Properties::setDirectoryPath(const std::string& path)
  910. {
  911. if (_dirPath == NULL)
  912. {
  913. _dirPath = new std::string(path);
  914. }
  915. else
  916. {
  917. _dirPath->assign(path);
  918. }
  919. }
  920. void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath)
  921. {
  922. // If the url references a specific namespace within the file,
  923. // calculate the full namespace path to the final namespace.
  924. size_t loc = urlString.rfind("#");
  925. if (loc != std::string::npos)
  926. {
  927. fileString = urlString.substr(0, loc);
  928. std::string namespacePathString = urlString.substr(loc + 1);
  929. while ((loc = namespacePathString.find("/")) != std::string::npos)
  930. {
  931. namespacePath.push_back(namespacePathString.substr(0, loc));
  932. namespacePathString = namespacePathString.substr(loc + 1);
  933. }
  934. namespacePath.push_back(namespacePathString);
  935. }
  936. else
  937. {
  938. fileString = urlString;
  939. }
  940. }
  941. Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath)
  942. {
  943. // If the url references a specific namespace within the file,
  944. // return the specified namespace or notify the user if it cannot be found.
  945. if (namespacePath.size() > 0)
  946. {
  947. size_t size = namespacePath.size();
  948. properties->rewind();
  949. Properties* iter = properties->getNextNamespace();
  950. for (size_t i = 0; i < size; )
  951. {
  952. while (true)
  953. {
  954. if (iter == NULL)
  955. {
  956. GP_WARN("Failed to load properties object from url.");
  957. return NULL;
  958. }
  959. if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
  960. {
  961. if (i != size - 1)
  962. {
  963. properties = iter->getNextNamespace();
  964. iter = properties;
  965. }
  966. else
  967. properties = iter;
  968. i++;
  969. break;
  970. }
  971. iter = properties->getNextNamespace();
  972. }
  973. }
  974. return properties;
  975. }
  976. else
  977. return properties;
  978. }
  979. bool Properties::parseVector2(const char* str, Vector2* out)
  980. {
  981. if (str)
  982. {
  983. float x, y;
  984. if (sscanf(str, "%f,%f", &x, &y) == 2)
  985. {
  986. if (out)
  987. out->set(x, y);
  988. return true;
  989. }
  990. else
  991. {
  992. GP_WARN("Error attempting to parse property as a two-dimensional vector: %s", str);
  993. }
  994. }
  995. if (out)
  996. out->set(0.0f, 0.0f);
  997. return false;
  998. }
  999. bool Properties::parseVector3(const char* str, Vector3* out)
  1000. {
  1001. if (str)
  1002. {
  1003. float x, y, z;
  1004. if (sscanf(str, "%f,%f,%f", &x, &y, &z) == 3)
  1005. {
  1006. if (out)
  1007. out->set(x, y, z);
  1008. return true;
  1009. }
  1010. else
  1011. {
  1012. GP_WARN("Error attempting to parse property as a three-dimensional vector: %s", str);
  1013. }
  1014. }
  1015. if (out)
  1016. out->set(0.0f, 0.0f, 0.0f);
  1017. return false;
  1018. }
  1019. bool Properties::parseVector4(const char* str, Vector4* out)
  1020. {
  1021. if (str)
  1022. {
  1023. float x, y, z, w;
  1024. if (sscanf(str, "%f,%f,%f,%f", &x, &y, &z, &w) == 4)
  1025. {
  1026. if (out)
  1027. out->set(x, y, z, w);
  1028. return true;
  1029. }
  1030. else
  1031. {
  1032. GP_WARN("Error attempting to parse property as a four-dimensional vector: %s", str);
  1033. }
  1034. }
  1035. if (out)
  1036. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  1037. return false;
  1038. }
  1039. bool Properties::parseAxisAngle(const char* str, Quaternion* out)
  1040. {
  1041. if (str)
  1042. {
  1043. float x, y, z, theta;
  1044. if (sscanf(str, "%f,%f,%f,%f", &x, &y, &z, &theta) == 4)
  1045. {
  1046. if (out)
  1047. out->set(Vector3(x, y, z), MATH_DEG_TO_RAD(theta));
  1048. return true;
  1049. }
  1050. else
  1051. {
  1052. GP_WARN("Error attempting to parse property as an axis-angle rotation: %s", str);
  1053. }
  1054. }
  1055. if (out)
  1056. out->set(0.0f, 0.0f, 0.0f, 1.0f);
  1057. return false;
  1058. }
  1059. bool Properties::parseColor(const char* str, Vector3* out)
  1060. {
  1061. if (str)
  1062. {
  1063. if (strlen(str) == 7 && str[0] == '#')
  1064. {
  1065. // Read the string into an int as hex.
  1066. unsigned int color;
  1067. if (sscanf(str + 1, "%x", &color) == 1)
  1068. {
  1069. if (out)
  1070. out->set(Vector3::fromColor(color));
  1071. return true;
  1072. }
  1073. else
  1074. {
  1075. // Invalid format
  1076. GP_WARN("Error attempting to parse property as an RGB color: %s", str);
  1077. }
  1078. }
  1079. else
  1080. {
  1081. // Not a color string.
  1082. GP_WARN("Error attempting to parse property as an RGB color (not specified as a color string): %s", str);
  1083. }
  1084. }
  1085. if (out)
  1086. out->set(0.0f, 0.0f, 0.0f);
  1087. return false;
  1088. }
  1089. bool Properties::parseColor(const char* str, Vector4* out)
  1090. {
  1091. if (str)
  1092. {
  1093. if (strlen(str) == 9 && str[0] == '#')
  1094. {
  1095. // Read the string into an int as hex.
  1096. unsigned int color;
  1097. if (sscanf(str + 1, "%x", &color) == 1)
  1098. {
  1099. if (out)
  1100. out->set(Vector4::fromColor(color));
  1101. return true;
  1102. }
  1103. else
  1104. {
  1105. // Invalid format
  1106. GP_WARN("Error attempting to parse property as an RGBA color: %s", str);
  1107. }
  1108. }
  1109. else
  1110. {
  1111. // Not a color string.
  1112. GP_WARN("Error attempting to parse property as an RGBA color (not specified as a color string): %s", str);
  1113. }
  1114. }
  1115. if (out)
  1116. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  1117. return false;
  1118. }
  1119. }