Properties.cpp 29 KB

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