Properties.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  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. for (size_t i = 0, count = _namespaces.size(); i < count; ++i)
  290. {
  291. SAFE_DELETE(_namespaces[i]);
  292. }
  293. }
  294. void Properties::skipWhiteSpace(FILE* file)
  295. {
  296. int c;
  297. do
  298. {
  299. c = fgetc(file);
  300. } while (isspace(c));
  301. // If we are not at the end of the file, then since we found a
  302. // non-whitespace character, we put the cursor back in front of it.
  303. if (c != EOF)
  304. {
  305. if (fseek(file, -1, SEEK_CUR) != 0)
  306. {
  307. GP_ERROR("Failed to seek backwards one character after skipping whitespace.");
  308. }
  309. }
  310. }
  311. char* Properties::trimWhiteSpace(char *str)
  312. {
  313. if (str == NULL)
  314. {
  315. return str;
  316. }
  317. char *end;
  318. // Trim leading space.
  319. while (isspace(*str))
  320. str++;
  321. // All spaces?
  322. if (*str == 0)
  323. {
  324. return str;
  325. }
  326. // Trim trailing space.
  327. end = str + strlen(str) - 1;
  328. while (end > str && isspace(*end))
  329. end--;
  330. // Write new null terminator.
  331. *(end+1) = 0;
  332. return str;
  333. }
  334. void Properties::resolveInheritance(const char* id)
  335. {
  336. // Namespaces can be defined like so:
  337. // "name id : parentID { }"
  338. // This method merges data from the parent namespace into the child.
  339. // Get a top-level namespace.
  340. Properties* derived;
  341. if (id)
  342. {
  343. derived = getNamespace(id);
  344. }
  345. else
  346. {
  347. derived = getNextNamespace();
  348. }
  349. while (derived)
  350. {
  351. // If the namespace has a parent ID, find the parent.
  352. if (!derived->_parentID.empty())
  353. {
  354. Properties* parent = getNamespace(derived->_parentID.c_str());
  355. if (parent)
  356. {
  357. resolveInheritance(parent->getId());
  358. // Copy the child.
  359. Properties* overrides = new Properties(*derived);
  360. // Delete the child's data.
  361. for (size_t i = 0, count = derived->_namespaces.size(); i < count; i++)
  362. {
  363. SAFE_DELETE(derived->_namespaces[i]);
  364. }
  365. // Copy data from the parent into the child.
  366. derived->_properties = parent->_properties;
  367. derived->_namespaces = std::vector<Properties*>();
  368. std::vector<Properties*>::const_iterator itt;
  369. for (itt = parent->_namespaces.begin(); itt < parent->_namespaces.end(); ++itt)
  370. {
  371. GP_ASSERT(*itt);
  372. derived->_namespaces.push_back(new Properties(**itt));
  373. }
  374. derived->rewind();
  375. // Take the original copy of the child and override the data copied from the parent.
  376. derived->mergeWith(overrides);
  377. // Delete the child copy.
  378. SAFE_DELETE(overrides);
  379. }
  380. }
  381. // Resolve inheritance within this namespace.
  382. derived->resolveInheritance();
  383. // Get the next top-level namespace and check again.
  384. if (!id)
  385. {
  386. derived = getNextNamespace();
  387. }
  388. else
  389. {
  390. derived = NULL;
  391. }
  392. }
  393. }
  394. void Properties::mergeWith(Properties* overrides)
  395. {
  396. GP_ASSERT(overrides);
  397. // Overwrite or add each property found in child.
  398. char* value = new char[255];
  399. overrides->rewind();
  400. const char* name = overrides->getNextProperty(&value);
  401. while (name)
  402. {
  403. this->_properties[name] = value;
  404. name = overrides->getNextProperty(&value);
  405. }
  406. SAFE_DELETE_ARRAY(value);
  407. this->_propertiesItr = this->_properties.end();
  408. // Merge all common nested namespaces, add new ones.
  409. Properties* overridesNamespace = overrides->getNextNamespace();
  410. while (overridesNamespace)
  411. {
  412. bool merged = false;
  413. rewind();
  414. Properties* derivedNamespace = getNextNamespace();
  415. while (derivedNamespace)
  416. {
  417. if (strcmp(derivedNamespace->getNamespace(), overridesNamespace->getNamespace()) == 0 &&
  418. strcmp(derivedNamespace->getId(), overridesNamespace->getId()) == 0)
  419. {
  420. derivedNamespace->mergeWith(overridesNamespace);
  421. merged = true;
  422. }
  423. derivedNamespace = getNextNamespace();
  424. }
  425. if (!merged)
  426. {
  427. // Add this new namespace.
  428. Properties* newNamespace = new Properties(*overridesNamespace);
  429. this->_namespaces.push_back(newNamespace);
  430. this->_namespacesItr = this->_namespaces.end();
  431. }
  432. overridesNamespace = overrides->getNextNamespace();
  433. }
  434. }
  435. const char* Properties::getNextProperty(char** value)
  436. {
  437. if (_propertiesItr == _properties.end())
  438. {
  439. // Restart from the beginning
  440. _propertiesItr = _properties.begin();
  441. }
  442. else
  443. {
  444. // Move to the next property
  445. ++_propertiesItr;
  446. }
  447. if (_propertiesItr != _properties.end())
  448. {
  449. const std::string& name = _propertiesItr->first;
  450. if (!name.empty())
  451. {
  452. if (value)
  453. {
  454. strcpy(*value, _propertiesItr->second.c_str());
  455. }
  456. return name.c_str();
  457. }
  458. }
  459. return NULL;
  460. }
  461. Properties* Properties::getNextNamespace()
  462. {
  463. if (_namespacesItr == _namespaces.end())
  464. {
  465. // Restart from the beginning
  466. _namespacesItr = _namespaces.begin();
  467. }
  468. else
  469. {
  470. ++_namespacesItr;
  471. }
  472. if (_namespacesItr != _namespaces.end())
  473. {
  474. Properties* ns = *_namespacesItr;
  475. return ns;
  476. }
  477. return NULL;
  478. }
  479. void Properties::rewind()
  480. {
  481. _propertiesItr = _properties.end();
  482. _namespacesItr = _namespaces.end();
  483. }
  484. Properties* Properties::getNamespace(const char* id, bool searchNames) const
  485. {
  486. GP_ASSERT(id);
  487. Properties* ret = NULL;
  488. std::vector<Properties*>::const_iterator it;
  489. for (it = _namespaces.begin(); it < _namespaces.end(); ++it)
  490. {
  491. ret = *it;
  492. if (strcmp(searchNames ? ret->_namespace.c_str() : ret->_id.c_str(), id) == 0)
  493. {
  494. return ret;
  495. }
  496. // Search recursively.
  497. ret = ret->getNamespace(id, searchNames);
  498. if (ret != NULL)
  499. {
  500. return ret;
  501. }
  502. }
  503. return ret;
  504. }
  505. const char* Properties::getNamespace() const
  506. {
  507. return _namespace.c_str();
  508. }
  509. const char* Properties::getId() const
  510. {
  511. return _id.c_str();
  512. }
  513. bool Properties::exists(const char* name) const
  514. {
  515. GP_ASSERT(name);
  516. return _properties.find(name) != _properties.end();
  517. }
  518. static const bool isStringNumeric(const char* str)
  519. {
  520. GP_ASSERT(str);
  521. // The first character may be '-'
  522. if (*str == '-')
  523. str++;
  524. // The first character after the sign must be a digit
  525. if (!isdigit(*str))
  526. return false;
  527. str++;
  528. // All remaining characters must be digits, with a single decimal (.) permitted
  529. unsigned int decimalCount = 0;
  530. while (*str)
  531. {
  532. if (!isdigit(*str))
  533. {
  534. if (*str == '.' && decimalCount == 0)
  535. {
  536. // Max of 1 decimal allowed
  537. decimalCount++;
  538. }
  539. else
  540. {
  541. return false;
  542. }
  543. }
  544. str++;
  545. }
  546. return true;
  547. }
  548. Properties::Type Properties::getType(const char* name) const
  549. {
  550. const char* value = getString(name);
  551. if (!value)
  552. {
  553. return Properties::NONE;
  554. }
  555. // Parse the value to determine the format
  556. unsigned int commaCount = 0;
  557. char* valuePtr = const_cast<char*>(value);
  558. while (valuePtr = strchr(valuePtr, ','))
  559. {
  560. valuePtr++;
  561. commaCount++;
  562. }
  563. switch (commaCount)
  564. {
  565. case 0:
  566. return isStringNumeric(value) ? Properties::NUMBER : Properties::STRING;
  567. case 1:
  568. return Properties::VECTOR2;
  569. case 2:
  570. return Properties::VECTOR3;
  571. case 3:
  572. return Properties::VECTOR4;
  573. case 15:
  574. return Properties::MATRIX;
  575. default:
  576. return Properties::STRING;
  577. }
  578. }
  579. const char* Properties::getString(const char* name) const
  580. {
  581. if (name)
  582. {
  583. std::map<std::string, std::string>::const_iterator itr = _properties.find(name);
  584. if (itr != _properties.end())
  585. {
  586. return itr->second.c_str();
  587. }
  588. }
  589. else
  590. {
  591. if (_propertiesItr != _properties.end())
  592. {
  593. return _propertiesItr->second.c_str();
  594. }
  595. }
  596. return NULL;
  597. }
  598. bool Properties::getBool(const char* name, bool defaultValue) const
  599. {
  600. const char* valueString = getString(name);
  601. if (valueString)
  602. {
  603. return (strcmp(valueString, "true") == 0);
  604. }
  605. return defaultValue;
  606. }
  607. int Properties::getInt(const char* name) const
  608. {
  609. const char* valueString = getString(name);
  610. if (valueString)
  611. {
  612. int value;
  613. int scanned;
  614. scanned = sscanf(valueString, "%d", &value);
  615. if (scanned != 1)
  616. {
  617. GP_ERROR("Error attempting to parse property '%s' as an integer.", name);
  618. return 0;
  619. }
  620. return value;
  621. }
  622. return 0;
  623. }
  624. float Properties::getFloat(const char* name) const
  625. {
  626. const char* valueString = getString(name);
  627. if (valueString)
  628. {
  629. float value;
  630. int scanned;
  631. scanned = sscanf(valueString, "%f", &value);
  632. if (scanned != 1)
  633. {
  634. GP_ERROR("Error attempting to parse property '%s' as a float.", name);
  635. return 0.0f;
  636. }
  637. return value;
  638. }
  639. return 0.0f;
  640. }
  641. long Properties::getLong(const char* name) const
  642. {
  643. const char* valueString = getString(name);
  644. if (valueString)
  645. {
  646. long value;
  647. int scanned;
  648. scanned = sscanf(valueString, "%ld", &value);
  649. if (scanned != 1)
  650. {
  651. GP_ERROR("Error attempting to parse property '%s' as a long integer.", name);
  652. return 0L;
  653. }
  654. return value;
  655. }
  656. return 0L;
  657. }
  658. bool Properties::getMatrix(const char* name, Matrix* out) const
  659. {
  660. GP_ASSERT(out);
  661. const char* valueString = getString(name);
  662. if (valueString)
  663. {
  664. float m[16];
  665. int scanned;
  666. scanned = sscanf(valueString, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f",
  667. &m[0], &m[1], &m[2], &m[3], &m[4], &m[5], &m[6], &m[7],
  668. &m[8], &m[9], &m[10], &m[11], &m[12], &m[13], &m[14], &m[15]);
  669. if (scanned != 16)
  670. {
  671. GP_ERROR("Error attempting to parse property '%s' as a matrix.", name);
  672. out->setIdentity();
  673. return false;
  674. }
  675. out->set(m);
  676. return true;
  677. }
  678. out->setIdentity();
  679. return false;
  680. }
  681. bool Properties::getVector2(const char* name, Vector2* out) const
  682. {
  683. GP_ASSERT(out);
  684. const char* valueString = getString(name);
  685. if (valueString)
  686. {
  687. float x, y;
  688. int scanned;
  689. scanned = sscanf(valueString, "%f,%f", &x, &y);
  690. if (scanned != 2)
  691. {
  692. GP_ERROR("Error attempting to parse property '%s' as a two-dimensional vector.", name);
  693. out->set(0.0f, 0.0f);
  694. return false;
  695. }
  696. out->set(x, y);
  697. return true;
  698. }
  699. out->set(0.0f, 0.0f);
  700. return false;
  701. }
  702. bool Properties::getVector3(const char* name, Vector3* out) const
  703. {
  704. GP_ASSERT(out);
  705. const char* valueString = getString(name);
  706. if (valueString)
  707. {
  708. float x, y, z;
  709. int scanned;
  710. scanned = sscanf(valueString, "%f,%f,%f", &x, &y, &z);
  711. if (scanned != 3)
  712. {
  713. GP_ERROR("Error attempting to parse property '%s' as a three-dimensional vector.", name);
  714. out->set(0.0f, 0.0f, 0.0f);
  715. return false;
  716. }
  717. out->set(x, y, z);
  718. return true;
  719. }
  720. out->set(0.0f, 0.0f, 0.0f);
  721. return false;
  722. }
  723. bool Properties::getVector4(const char* name, Vector4* out) const
  724. {
  725. GP_ASSERT(out);
  726. const char* valueString = getString(name);
  727. if (valueString)
  728. {
  729. float x, y, z, w;
  730. int scanned;
  731. scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &w);
  732. if (scanned != 4)
  733. {
  734. GP_ERROR("Error attempting to parse property '%s' as a four-dimensional vector.", name);
  735. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  736. return false;
  737. }
  738. out->set(x, y, z, w);
  739. return true;
  740. }
  741. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  742. return false;
  743. }
  744. bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) const
  745. {
  746. GP_ASSERT(out);
  747. const char* valueString = getString(name);
  748. if (valueString)
  749. {
  750. float x, y, z, theta;
  751. int scanned;
  752. scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &theta);
  753. if (scanned != 4)
  754. {
  755. GP_ERROR("Error attempting to parse property '%s' as an axis-angle rotation.", name);
  756. out->set(0.0f, 0.0f, 0.0f, 1.0f);
  757. return false;
  758. }
  759. out->set(Vector3(x, y, z), MATH_DEG_TO_RAD(theta));
  760. return true;
  761. }
  762. out->set(0.0f, 0.0f, 0.0f, 1.0f);
  763. return false;
  764. }
  765. bool Properties::getColor(const char* name, Vector3* out) const
  766. {
  767. GP_ASSERT(out);
  768. const char* valueString = getString(name);
  769. if (valueString)
  770. {
  771. if (strlen(valueString) != 7 ||
  772. valueString[0] != '#')
  773. {
  774. // Not a color string.
  775. GP_ERROR("Error attempting to parse property '%s' as an RGB color (not specified as a color string).", name);
  776. out->set(0.0f, 0.0f, 0.0f);
  777. return false;
  778. }
  779. // Read the string into an int as hex.
  780. unsigned int color;
  781. if (sscanf(valueString+1, "%x", &color) != 1)
  782. {
  783. GP_ERROR("Error attempting to parse property '%s' as an RGB color.", name);
  784. out->set(0.0f, 0.0f, 0.0f);
  785. return false;
  786. }
  787. out->set(Vector3::fromColor(color));
  788. return true;
  789. }
  790. out->set(0.0f, 0.0f, 0.0f);
  791. return false;
  792. }
  793. bool Properties::getColor(const char* name, Vector4* out) const
  794. {
  795. GP_ASSERT(out);
  796. const char* valueString = getString(name);
  797. if (valueString)
  798. {
  799. if (strlen(valueString) != 9 ||
  800. valueString[0] != '#')
  801. {
  802. // Not a color string.
  803. GP_ERROR("Error attempting to parse property '%s' as an RGBA color (not specified as a color string).", name);
  804. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  805. return false;
  806. }
  807. // Read the string into an int as hex.
  808. unsigned int color;
  809. if (sscanf(valueString+1, "%x", &color) != 1)
  810. {
  811. GP_ERROR("Error attempting to parse property '%s' as an RGBA color.", name);
  812. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  813. return false;
  814. }
  815. out->set(Vector4::fromColor(color));
  816. return true;
  817. }
  818. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  819. return false;
  820. }
  821. Properties* Properties::clone()
  822. {
  823. Properties* p = new Properties();
  824. p->_namespace = _namespace;
  825. p->_id = _id;
  826. p->_parentID = _parentID;
  827. p->_properties = _properties;
  828. p->_propertiesItr = p->_properties.end();
  829. for (size_t i = 0, count = _namespaces.size(); i < count; i++)
  830. {
  831. GP_ASSERT(_namespaces[i]);
  832. p->_namespaces.push_back(_namespaces[i]->clone());
  833. }
  834. p->_namespacesItr = p->_namespaces.end();
  835. return p;
  836. }
  837. void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath)
  838. {
  839. // If the url references a specific namespace within the file,
  840. // calculate the full namespace path to the final namespace.
  841. size_t loc = urlString.rfind("#");
  842. if (loc != std::string::npos)
  843. {
  844. fileString = urlString.substr(0, loc);
  845. std::string namespacePathString = urlString.substr(loc + 1);
  846. while ((loc = namespacePathString.find("/")) != std::string::npos)
  847. {
  848. namespacePath.push_back(namespacePathString.substr(0, loc));
  849. namespacePathString = namespacePathString.substr(loc + 1);
  850. }
  851. namespacePath.push_back(namespacePathString);
  852. }
  853. else
  854. {
  855. fileString = urlString;
  856. }
  857. }
  858. Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath)
  859. {
  860. // If the url references a specific namespace within the file,
  861. // return the specified namespace or notify the user if it cannot be found.
  862. if (namespacePath.size() > 0)
  863. {
  864. size_t size = namespacePath.size();
  865. properties->rewind();
  866. Properties* iter = properties->getNextNamespace();
  867. for (size_t i = 0; i < size; )
  868. {
  869. while (true)
  870. {
  871. if (iter == NULL)
  872. {
  873. GP_ERROR("Failed to load properties object from url.");
  874. return NULL;
  875. }
  876. if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
  877. {
  878. if (i != size - 1)
  879. {
  880. properties = iter->getNextNamespace();
  881. iter = properties;
  882. }
  883. else
  884. properties = iter;
  885. i++;
  886. break;
  887. }
  888. iter = properties->getNextNamespace();
  889. }
  890. }
  891. return properties;
  892. }
  893. else
  894. return properties;
  895. }
  896. }