Properties.cpp 23 KB

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