Properties.cpp 29 KB

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