Properties.cpp 30 KB

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