Properties.cpp 29 KB

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