1
0

CCProperties.cpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333
  1. /**
  2. Copyright 2013 BlackBerry Inc.
  3. Copyright (c) 2015-2017 Chukong Technologies
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. Original file from GamePlay3D: http://gameplay3d.org
  14. This file was modified to fit the cocos2d-x project
  15. */
  16. #include "base/CCProperties.h"
  17. #include <string.h>
  18. #include "platform/CCPlatformMacros.h"
  19. #include "platform/CCFileUtils.h"
  20. #include "math/Vec2.h"
  21. #include "math/Vec3.h"
  22. #include "math/Vec4.h"
  23. #include "math/Mat4.h"
  24. #include "math/Quaternion.h"
  25. #include "base/ccUTF8.h"
  26. #include "base/CCData.h"
  27. USING_NS_CC;
  28. // Utility functions (shared with SceneLoader).
  29. /** @script{ignore} */
  30. void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
  31. /** @script{ignore} */
  32. Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
  33. Properties::Properties()
  34. : _dataIdx(nullptr), _data(nullptr), _variables(nullptr), _dirPath(nullptr), _parent(nullptr)
  35. {
  36. _properties.reserve(32);
  37. }
  38. Properties::Properties(const Properties& copy)
  39. : _dataIdx(copy._dataIdx), _data(copy._data), _namespace(copy._namespace),
  40. _id(copy._id), _parentID(copy._parentID), _properties(copy._properties),
  41. _variables(nullptr), _dirPath(nullptr), _parent(copy._parent)
  42. {
  43. setDirectoryPath(copy._dirPath);
  44. for (const auto space: copy._namespaces)
  45. {
  46. _namespaces.push_back(new (std::nothrow) Properties(*space));
  47. }
  48. rewind();
  49. }
  50. Properties::Properties(Data* data, ssize_t* dataIdx)
  51. : _dataIdx(dataIdx), _data(data), _variables(NULL), _dirPath(NULL), _parent(NULL)
  52. {
  53. readProperties();
  54. rewind();
  55. }
  56. Properties::Properties(Data* data, ssize_t* dataIdx, const std::string& name, const char* id, const char* parentID, Properties* parent)
  57. : _dataIdx(dataIdx), _data(data), _namespace(name), _variables(NULL), _dirPath(NULL), _parent(parent)
  58. {
  59. if (id)
  60. {
  61. _id = id;
  62. }
  63. if (parentID)
  64. {
  65. _parentID = parentID;
  66. }
  67. readProperties();
  68. rewind();
  69. }
  70. Properties* Properties::createNonRefCounted(const std::string& url)
  71. {
  72. if (url.size() == 0)
  73. {
  74. CCLOGERROR("Attempting to create a Properties object from an empty URL!");
  75. return nullptr;
  76. }
  77. // Calculate the file and full namespace path from the specified url.
  78. std::string urlString = url;
  79. std::string fileString;
  80. std::vector<std::string> namespacePath;
  81. calculateNamespacePath(urlString, fileString, namespacePath);
  82. // data will be released automatically when 'data' goes out of scope
  83. // so we pass data as weak pointer
  84. auto data = FileUtils::getInstance()->getDataFromFile(fileString);
  85. ssize_t dataIdx = 0;
  86. Properties* properties = new (std::nothrow) Properties(&data, &dataIdx);
  87. properties->resolveInheritance();
  88. // Get the specified properties object.
  89. Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
  90. if (!p)
  91. {
  92. CCLOGWARN("Failed to load properties from url '%s'.", url.c_str());
  93. CC_SAFE_DELETE(properties);
  94. return nullptr;
  95. }
  96. // If the loaded properties object is not the root namespace,
  97. // then we have to clone it and delete the root namespace
  98. // so that we don't leak memory.
  99. if (p != properties)
  100. {
  101. p = p->clone();
  102. CC_SAFE_DELETE(properties);
  103. }
  104. // XXX
  105. // p->setDirectoryPath(FileSystem::getDirectoryName(fileString));
  106. p->setDirectoryPath("");
  107. return p;
  108. }
  109. static bool isVariable(const char* str, char* outName, size_t outSize)
  110. {
  111. size_t len = strlen(str);
  112. if (len > 3 && str[0] == '$' && str[1] == '{' && str[len - 1] == '}')
  113. {
  114. size_t size = len - 3;
  115. if (size > (outSize - 1))
  116. size = outSize - 1;
  117. strncpy(outName, str + 2, len - 3);
  118. outName[len - 3] = 0;
  119. return true;
  120. }
  121. return false;
  122. }
  123. void Properties::readProperties()
  124. {
  125. CCASSERT(_data->getSize() >0, "Invalid data");
  126. char line[2048];
  127. char variable[256];
  128. int c;
  129. char* name;
  130. char* value;
  131. char* parentID;
  132. char* rc;
  133. char* rcc;
  134. char* rccc;
  135. bool comment = false;
  136. while (true)
  137. {
  138. // Skip whitespace at the start of lines
  139. skipWhiteSpace();
  140. // Stop when we have reached the end of the file.
  141. if (eof())
  142. break;
  143. // Read the next line.
  144. rc = readLine(line, 2048);
  145. if (rc == NULL)
  146. {
  147. CCLOGERROR("Error reading line from file.");
  148. return;
  149. }
  150. // Ignore comments
  151. if (comment)
  152. {
  153. // Check for end of multi-line comment at either start or end of line
  154. if (strncmp(line, "*/", 2) == 0)
  155. comment = false;
  156. else
  157. {
  158. trimWhiteSpace(line);
  159. const auto len = strlen(line);
  160. if (len >= 2 && strncmp(line + (len - 2), "*/", 2) == 0)
  161. comment = false;
  162. }
  163. }
  164. else if (strncmp(line, "/*", 2) == 0)
  165. {
  166. // Start of multi-line comment (must be at start of line)
  167. comment = true;
  168. }
  169. else if (strncmp(line, "//", 2) != 0)
  170. {
  171. // If an '=' appears on this line, parse it as a name/value pair.
  172. // Note: strchr() has to be called before strtok(), or a backup of line has to be kept.
  173. rc = strchr(line, '=');
  174. if (rc != NULL)
  175. {
  176. // First token should be the property name.
  177. name = strtok(line, "=");
  178. if (name == NULL)
  179. {
  180. CCLOGERROR("Error parsing properties file: attribute without name.");
  181. return;
  182. }
  183. // Remove white-space from name.
  184. name = trimWhiteSpace(name);
  185. // Scan for next token, the property's value.
  186. value = strtok(NULL, "");
  187. if (value == NULL)
  188. {
  189. CCLOGERROR("Error parsing properties file: attribute with name ('%s') but no value.", name);
  190. return;
  191. }
  192. // Remove white-space from value.
  193. value = trimWhiteSpace(value);
  194. // Is this a variable assignment?
  195. if (isVariable(name, variable, 256))
  196. {
  197. setVariable(variable, value);
  198. }
  199. else
  200. {
  201. // Normal name/value pair
  202. _properties.push_back(Property(name, value));
  203. }
  204. }
  205. else
  206. {
  207. parentID = NULL;
  208. // Get the last character on the line (ignoring whitespace).
  209. const char* lineEnd = trimWhiteSpace(line) + (strlen(trimWhiteSpace(line)) - 1);
  210. // This line might begin or end a namespace,
  211. // or it might be a key/value pair without '='.
  212. // Check for '{' on same line.
  213. rc = strchr(line, '{');
  214. // Check for inheritance: ':'
  215. rcc = strchr(line, ':');
  216. // Check for '}' on same line.
  217. rccc = strchr(line, '}');
  218. // Get the name of the namespace.
  219. name = strtok(line, " \t\n{");
  220. name = trimWhiteSpace(name);
  221. if (name == NULL)
  222. {
  223. CCLOGERROR("Error parsing properties file: failed to determine a valid token for line '%s'.", line);
  224. return;
  225. }
  226. else if (name[0] == '}')
  227. {
  228. // End of namespace.
  229. return;
  230. }
  231. // Get its ID if it has one.
  232. value = strtok(NULL, ":{");
  233. value = trimWhiteSpace(value);
  234. // Get its parent ID if it has one.
  235. if (rcc != NULL)
  236. {
  237. parentID = strtok(NULL, "{");
  238. parentID = trimWhiteSpace(parentID);
  239. }
  240. if (value != NULL && value[0] == '{')
  241. {
  242. // If the namespace ends on this line, seek back to right before the '}' character.
  243. if (rccc && rccc == lineEnd)
  244. {
  245. if (seekFromCurrent(-1) == false)
  246. {
  247. CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
  248. return;
  249. }
  250. while (readChar() != '}')
  251. {
  252. if (seekFromCurrent(-2) == false)
  253. {
  254. CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
  255. return;
  256. }
  257. }
  258. if (seekFromCurrent(-1) == false)
  259. {
  260. CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
  261. return;
  262. }
  263. }
  264. // New namespace without an ID.
  265. Properties* space = new (std::nothrow) Properties(_data, _dataIdx, name, NULL, parentID, this);
  266. _namespaces.push_back(space);
  267. // If the namespace ends on this line, seek to right after the '}' character.
  268. if (rccc && rccc == lineEnd)
  269. {
  270. if (seekFromCurrent(1) == false)
  271. {
  272. CCLOGERROR("Failed to seek to immediately after a '}' character in properties file.");
  273. return;
  274. }
  275. }
  276. }
  277. else
  278. {
  279. // If '{' appears on the same line.
  280. if (rc != NULL)
  281. {
  282. // If the namespace ends on this line, seek back to right before the '}' character.
  283. if (rccc && rccc == lineEnd)
  284. {
  285. if (seekFromCurrent(-1) == false)
  286. {
  287. CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
  288. return;
  289. }
  290. while (readChar() != '}')
  291. {
  292. if (seekFromCurrent(-2) == false)
  293. {
  294. CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
  295. return;
  296. }
  297. }
  298. if (seekFromCurrent(-1) == false)
  299. {
  300. CCLOGERROR("Failed to seek back to before a '}' character in properties file.");
  301. return;
  302. }
  303. }
  304. // Create new namespace.
  305. Properties* space = new (std::nothrow) Properties(_data, _dataIdx, name, value, parentID, this);
  306. _namespaces.push_back(space);
  307. // If the namespace ends on this line, seek to right after the '}' character.
  308. if (rccc && rccc == lineEnd)
  309. {
  310. if (seekFromCurrent(1) == false)
  311. {
  312. CCLOGERROR("Failed to seek to immediately after a '}' character in properties file.");
  313. return;
  314. }
  315. }
  316. }
  317. else
  318. {
  319. // Find out if the next line starts with "{"
  320. skipWhiteSpace();
  321. c = readChar();
  322. if (c == '{')
  323. {
  324. // Create new namespace.
  325. Properties* space = new (std::nothrow) Properties(_data, _dataIdx, name, value, parentID, this);
  326. _namespaces.push_back(space);
  327. }
  328. else
  329. {
  330. // Back up from fgetc()
  331. if (seekFromCurrent(-1) == false)
  332. CCLOGERROR("Failed to seek backwards a single character after testing if the next line starts with '{'.");
  333. // Store "name value" as a name/value pair, or even just "name".
  334. if (value != NULL)
  335. {
  336. _properties.push_back(Property(name, value));
  337. }
  338. else
  339. {
  340. _properties.push_back(Property(name, ""));
  341. }
  342. }
  343. }
  344. }
  345. }
  346. }
  347. }
  348. }
  349. Properties::~Properties()
  350. {
  351. CC_SAFE_DELETE(_dirPath);
  352. for (size_t i = 0, count = _namespaces.size(); i < count; ++i)
  353. {
  354. CC_SAFE_DELETE(_namespaces[i]);
  355. }
  356. CC_SAFE_DELETE(_variables);
  357. }
  358. //
  359. // Stream simulation
  360. //
  361. signed char Properties::readChar()
  362. {
  363. if (eof())
  364. return EOF;
  365. return _data->_bytes[(*_dataIdx)++];
  366. }
  367. char* Properties::readLine(char* output, int num)
  368. {
  369. if (eof())
  370. return nullptr;
  371. // little optimization: avoid unneeded dereferences
  372. const ssize_t dataIdx = *_dataIdx;
  373. int i;
  374. for (i=0; i<num && dataIdx+i < _data->_size; i++)
  375. {
  376. auto c = _data->_bytes[dataIdx+i];
  377. if (c == '\n')
  378. break;
  379. output[i] = c;
  380. }
  381. output[i] = '\0';
  382. // restore value
  383. *_dataIdx = dataIdx+i;
  384. return output;
  385. }
  386. bool Properties::seekFromCurrent(int offset)
  387. {
  388. (*_dataIdx) += offset;
  389. return (!eof() && *_dataIdx >= 0);
  390. }
  391. bool Properties::eof()
  392. {
  393. return (*_dataIdx >= _data->_size);
  394. }
  395. void Properties::skipWhiteSpace()
  396. {
  397. signed char c;
  398. do
  399. {
  400. c = readChar();
  401. } while (isspace(c) && c != EOF);
  402. // If we are not at the end of the file, then since we found a
  403. // non-whitespace character, we put the cursor back in front of it.
  404. if (c != EOF)
  405. {
  406. if (seekFromCurrent(-1) == false)
  407. {
  408. CCLOGERROR("Failed to seek backwards one character after skipping whitespace.");
  409. }
  410. }
  411. }
  412. char* Properties::trimWhiteSpace(char *str)
  413. {
  414. if (str == NULL)
  415. {
  416. return str;
  417. }
  418. // Trim leading space.
  419. while (*str != '\0' && isspace(*str))
  420. str++;
  421. // All spaces?
  422. if (*str == 0)
  423. {
  424. return str;
  425. }
  426. // Trim trailing space.
  427. char *end = str + strlen(str) - 1;
  428. while (end > str && isspace(*end))
  429. end--;
  430. // Write new null terminator.
  431. *(end+1) = 0;
  432. return str;
  433. }
  434. void Properties::resolveInheritance(const char* id)
  435. {
  436. // Namespaces can be defined like so:
  437. // "name id : parentID { }"
  438. // This method merges data from the parent namespace into the child.
  439. // Get a top-level namespace.
  440. Properties* derived;
  441. if (id)
  442. {
  443. derived = getNamespace(id);
  444. }
  445. else
  446. {
  447. derived = getNextNamespace();
  448. }
  449. while (derived)
  450. {
  451. // If the namespace has a parent ID, find the parent.
  452. if (!derived->_parentID.empty())
  453. {
  454. Properties* parent = getNamespace(derived->_parentID.c_str());
  455. if (parent)
  456. {
  457. resolveInheritance(parent->getId());
  458. // Copy the child.
  459. Properties* overrides = new (std::nothrow) Properties(*derived);
  460. // Delete the child's data.
  461. for (size_t i = 0, count = derived->_namespaces.size(); i < count; i++)
  462. {
  463. CC_SAFE_DELETE(derived->_namespaces[i]);
  464. }
  465. // Copy data from the parent into the child.
  466. derived->_properties = parent->_properties;
  467. derived->_namespaces = std::vector<Properties*>();
  468. std::vector<Properties*>::const_iterator itt;
  469. for (const auto space: parent->_namespaces)
  470. {
  471. derived->_namespaces.push_back(new (std::nothrow) Properties(*space));
  472. }
  473. derived->rewind();
  474. // Take the original copy of the child and override the data copied from the parent.
  475. derived->mergeWith(overrides);
  476. // Delete the child copy.
  477. CC_SAFE_DELETE(overrides);
  478. }
  479. }
  480. // Resolve inheritance within this namespace.
  481. derived->resolveInheritance();
  482. // Get the next top-level namespace and check again.
  483. if (!id)
  484. {
  485. derived = getNextNamespace();
  486. }
  487. else
  488. {
  489. derived = NULL;
  490. }
  491. }
  492. }
  493. void Properties::mergeWith(Properties* overrides)
  494. {
  495. CCASSERT(overrides, "Invalid overrides");
  496. // Overwrite or add each property found in child.
  497. overrides->rewind();
  498. const char* name = overrides->getNextProperty();
  499. while (name)
  500. {
  501. this->setString(name, overrides->getString());
  502. name = overrides->getNextProperty();
  503. }
  504. this->_propertiesItr = this->_properties.end();
  505. // Merge all common nested namespaces, add new ones.
  506. Properties* overridesNamespace = overrides->getNextNamespace();
  507. while (overridesNamespace)
  508. {
  509. bool merged = false;
  510. rewind();
  511. Properties* derivedNamespace = getNextNamespace();
  512. while (derivedNamespace)
  513. {
  514. if (strcmp(derivedNamespace->getNamespace(), overridesNamespace->getNamespace()) == 0 &&
  515. strcmp(derivedNamespace->getId(), overridesNamespace->getId()) == 0)
  516. {
  517. derivedNamespace->mergeWith(overridesNamespace);
  518. merged = true;
  519. }
  520. derivedNamespace = getNextNamespace();
  521. }
  522. if (!merged)
  523. {
  524. // Add this new namespace.
  525. Properties* newNamespace = new (std::nothrow) Properties(*overridesNamespace);
  526. this->_namespaces.push_back(newNamespace);
  527. this->_namespacesItr = this->_namespaces.end();
  528. }
  529. overridesNamespace = overrides->getNextNamespace();
  530. }
  531. }
  532. const char* Properties::getNextProperty()
  533. {
  534. if (_propertiesItr == _properties.end())
  535. {
  536. // Restart from the beginning
  537. _propertiesItr = _properties.begin();
  538. }
  539. else
  540. {
  541. // Move to the next property
  542. ++_propertiesItr;
  543. }
  544. return _propertiesItr == _properties.end() ? NULL : _propertiesItr->name.c_str();
  545. }
  546. Properties* Properties::getNextNamespace()
  547. {
  548. if (_namespacesItr == _namespaces.end())
  549. {
  550. // Restart from the beginning
  551. _namespacesItr = _namespaces.begin();
  552. }
  553. else
  554. {
  555. ++_namespacesItr;
  556. }
  557. if (_namespacesItr != _namespaces.end())
  558. {
  559. Properties* ns = *_namespacesItr;
  560. return ns;
  561. }
  562. return nullptr;
  563. }
  564. void Properties::rewind()
  565. {
  566. _propertiesItr = _properties.end();
  567. _namespacesItr = _namespaces.end();
  568. }
  569. Properties* Properties::getNamespace(const char* id, bool searchNames, bool recurse) const
  570. {
  571. CCASSERT(id, "invalid id");
  572. for (const auto& it : _namespaces)
  573. {
  574. Properties* p = it;
  575. if (strcmp(searchNames ? p->_namespace.c_str() : p->_id.c_str(), id) == 0)
  576. return p;
  577. if (recurse)
  578. {
  579. // Search recursively.
  580. p = p->getNamespace(id, searchNames, true);
  581. if (p)
  582. return p;
  583. }
  584. }
  585. return nullptr;
  586. }
  587. const char* Properties::getNamespace() const
  588. {
  589. return _namespace.c_str();
  590. }
  591. const char* Properties::getId() const
  592. {
  593. return _id.c_str();
  594. }
  595. bool Properties::exists(const char* name) const
  596. {
  597. if (name == NULL)
  598. return false;
  599. for (const auto& itr : _properties)
  600. {
  601. if (itr.name == name)
  602. return true;
  603. }
  604. return false;
  605. }
  606. static bool isStringNumeric(const char* str)
  607. {
  608. CCASSERT(str, "invalid str");
  609. // The first character may be '-'
  610. if (*str == '-')
  611. str++;
  612. // The first character after the sign must be a digit
  613. if (!isdigit(*str))
  614. return false;
  615. str++;
  616. // All remaining characters must be digits, with a single decimal (.) permitted
  617. unsigned int decimalCount = 0;
  618. while (*str)
  619. {
  620. if (!isdigit(*str))
  621. {
  622. if (*str == '.' && decimalCount == 0)
  623. {
  624. // Max of 1 decimal allowed
  625. decimalCount++;
  626. }
  627. else
  628. {
  629. return false;
  630. }
  631. }
  632. str++;
  633. }
  634. return true;
  635. }
  636. Properties::Type Properties::getType(const char* name) const
  637. {
  638. const char* value = getString(name);
  639. if (!value)
  640. {
  641. return Properties::NONE;
  642. }
  643. // Parse the value to determine the format
  644. unsigned int commaCount = 0;
  645. char* valuePtr = const_cast<char*>(value);
  646. while ((valuePtr = strchr(valuePtr, ',')))
  647. {
  648. valuePtr++;
  649. commaCount++;
  650. }
  651. switch (commaCount)
  652. {
  653. case 0:
  654. return isStringNumeric(value) ? Properties::NUMBER : Properties::STRING;
  655. case 1:
  656. return Properties::VECTOR2;
  657. case 2:
  658. return Properties::VECTOR3;
  659. case 3:
  660. return Properties::VECTOR4;
  661. case 15:
  662. return Properties::MATRIX;
  663. default:
  664. return Properties::STRING;
  665. }
  666. }
  667. const char* Properties::getString(const char* name, const char* defaultValue) const
  668. {
  669. char variable[256];
  670. const char* value = NULL;
  671. if (name)
  672. {
  673. // If 'name' is a variable, return the variable value
  674. if (isVariable(name, variable, 256))
  675. {
  676. return getVariable(variable, defaultValue);
  677. }
  678. for (const auto& itr : _properties)
  679. {
  680. if (itr.name == name)
  681. {
  682. value = itr.value.c_str();
  683. break;
  684. }
  685. }
  686. }
  687. else
  688. {
  689. // No name provided - get the value at the current iterator position
  690. if (_propertiesItr != _properties.end())
  691. {
  692. value = _propertiesItr->value.c_str();
  693. }
  694. }
  695. if (value)
  696. {
  697. // If the value references a variable, return the variable value
  698. if (isVariable(value, variable, 256))
  699. return getVariable(variable, defaultValue);
  700. return value;
  701. }
  702. return defaultValue;
  703. }
  704. bool Properties::setString(const char* name, const char* value)
  705. {
  706. if (name)
  707. {
  708. for (auto& itr : _properties)
  709. {
  710. if (itr.name == name)
  711. {
  712. // Update the first property that matches this name
  713. itr.value = value ? value : "";
  714. return true;
  715. }
  716. }
  717. // There is no property with this name, so add one
  718. _properties.push_back(Property(name, value ? value : ""));
  719. }
  720. else
  721. {
  722. // If there's a current property, set its value
  723. if (_propertiesItr == _properties.end())
  724. return false;
  725. _propertiesItr->value = value ? value : "";
  726. }
  727. return true;
  728. }
  729. bool Properties::getBool(const char* name, bool defaultValue) const
  730. {
  731. const char* valueString = getString(name);
  732. if (valueString)
  733. {
  734. return (strcmp(valueString, "true") == 0);
  735. }
  736. return defaultValue;
  737. }
  738. int Properties::getInt(const char* name) const
  739. {
  740. const char* valueString = getString(name);
  741. if (valueString)
  742. {
  743. int value;
  744. int scanned;
  745. scanned = sscanf(valueString, "%d", &value);
  746. if (scanned != 1)
  747. {
  748. CCLOGERROR("Error attempting to parse property '%s' as an integer.", name);
  749. return 0;
  750. }
  751. return value;
  752. }
  753. return 0;
  754. }
  755. float Properties::getFloat(const char* name) const
  756. {
  757. const char* valueString = getString(name);
  758. if (valueString)
  759. {
  760. float value;
  761. int scanned;
  762. scanned = sscanf(valueString, "%f", &value);
  763. if (scanned != 1)
  764. {
  765. CCLOGERROR("Error attempting to parse property '%s' as a float.", name);
  766. return 0.0f;
  767. }
  768. return value;
  769. }
  770. return 0.0f;
  771. }
  772. long Properties::getLong(const char* name) const
  773. {
  774. const char* valueString = getString(name);
  775. if (valueString)
  776. {
  777. long value;
  778. int scanned;
  779. scanned = sscanf(valueString, "%ld", &value);
  780. if (scanned != 1)
  781. {
  782. CCLOGERROR("Error attempting to parse property '%s' as a long integer.", name);
  783. return 0L;
  784. }
  785. return value;
  786. }
  787. return 0L;
  788. }
  789. bool Properties::getMat4(const char* name, Mat4* out) const
  790. {
  791. CCASSERT(out, "Invalid out");
  792. const char* valueString = getString(name);
  793. if (valueString)
  794. {
  795. float m[16];
  796. int scanned;
  797. scanned = sscanf(valueString, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f",
  798. &m[0], &m[1], &m[2], &m[3], &m[4], &m[5], &m[6], &m[7],
  799. &m[8], &m[9], &m[10], &m[11], &m[12], &m[13], &m[14], &m[15]);
  800. if (scanned != 16)
  801. {
  802. CCLOGERROR("Error attempting to parse property '%s' as a matrix.", name);
  803. out->setIdentity();
  804. return false;
  805. }
  806. out->set(m);
  807. return true;
  808. }
  809. out->setIdentity();
  810. return false;
  811. }
  812. bool Properties::getVec2(const char* name, Vec2* out) const
  813. {
  814. return parseVec2(getString(name), out);
  815. }
  816. bool Properties::getVec3(const char* name, Vec3* out) const
  817. {
  818. return parseVec3(getString(name), out);
  819. }
  820. bool Properties::getVec4(const char* name, Vec4* out) const
  821. {
  822. return parseVec4(getString(name), out);
  823. }
  824. bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) const
  825. {
  826. return parseAxisAngle(getString(name), out);
  827. }
  828. bool Properties::getColor(const char* name, Vec3* out) const
  829. {
  830. return parseColor(getString(name), out);
  831. }
  832. bool Properties::getColor(const char* name, Vec4* out) const
  833. {
  834. return parseColor(getString(name), out);
  835. }
  836. bool Properties::getPath(const char* name, std::string* path) const
  837. {
  838. CCASSERT(name && path, "Invalid name or path");
  839. const char* valueString = getString(name);
  840. if (valueString)
  841. {
  842. if (FileUtils::getInstance()->isFileExist(valueString))
  843. {
  844. path->assign(valueString);
  845. return true;
  846. }
  847. else
  848. {
  849. const Properties* prop = this;
  850. while (prop != NULL)
  851. {
  852. // Search for the file path relative to the bundle file
  853. const std::string* dirPath = prop->_dirPath;
  854. if (dirPath != NULL && !dirPath->empty())
  855. {
  856. std::string relativePath = *dirPath;
  857. relativePath.append(valueString);
  858. if (FileUtils::getInstance()->isFileExist(relativePath))
  859. {
  860. path->assign(relativePath);
  861. return true;
  862. }
  863. }
  864. prop = prop->_parent;
  865. }
  866. }
  867. }
  868. return false;
  869. }
  870. const char* Properties::getVariable(const char* name, const char* defaultValue) const
  871. {
  872. if (name == NULL)
  873. return defaultValue;
  874. // Search for variable in this Properties object
  875. if (_variables)
  876. {
  877. for (size_t i = 0, count = _variables->size(); i < count; ++i)
  878. {
  879. Property& prop = (*_variables)[i];
  880. if (prop.name == name)
  881. return prop.value.c_str();
  882. }
  883. }
  884. // Search for variable in parent Properties
  885. return _parent ? _parent->getVariable(name, defaultValue) : defaultValue;
  886. }
  887. void Properties::setVariable(const char* name, const char* value)
  888. {
  889. CCASSERT(name, "Invalid name");
  890. Property* prop = NULL;
  891. // Search for variable in this Properties object and parents
  892. Properties* current = const_cast<Properties*>(this);
  893. while (current)
  894. {
  895. if (current->_variables)
  896. {
  897. for (size_t i = 0, count = current->_variables->size(); i < count; ++i)
  898. {
  899. Property* p = &(*current->_variables)[i];
  900. if (p->name == name)
  901. {
  902. prop = p;
  903. break;
  904. }
  905. }
  906. }
  907. current = current->_parent;
  908. }
  909. if (prop)
  910. {
  911. // Found an existing property, set it
  912. prop->value = value ? value : "";
  913. }
  914. else
  915. {
  916. // Add a new variable with this name
  917. if (!_variables)
  918. _variables = new (std::nothrow) std::vector<Property>();
  919. _variables->push_back(Property(name, value ? value : ""));
  920. }
  921. }
  922. Properties* Properties::clone()
  923. {
  924. Properties* p = new (std::nothrow) Properties();
  925. p->_namespace = _namespace;
  926. p->_id = _id;
  927. p->_parentID = _parentID;
  928. p->_properties = _properties;
  929. p->_propertiesItr = p->_properties.end();
  930. p->setDirectoryPath(_dirPath);
  931. for (size_t i = 0, count = _namespaces.size(); i < count; i++)
  932. {
  933. CCASSERT(_namespaces[i], "Invalid namespace");
  934. Properties* child = _namespaces[i]->clone();
  935. p->_namespaces.push_back(child);
  936. child->_parent = p;
  937. }
  938. p->_namespacesItr = p->_namespaces.end();
  939. return p;
  940. }
  941. void Properties::setDirectoryPath(const std::string* path)
  942. {
  943. if (path)
  944. {
  945. setDirectoryPath(*path);
  946. }
  947. else
  948. {
  949. CC_SAFE_DELETE(_dirPath);
  950. }
  951. }
  952. void Properties::setDirectoryPath(const std::string& path)
  953. {
  954. if (_dirPath == NULL)
  955. {
  956. _dirPath = new (std::nothrow) std::string(path);
  957. }
  958. else
  959. {
  960. _dirPath->assign(path);
  961. }
  962. }
  963. void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath)
  964. {
  965. // If the url references a specific namespace within the file,
  966. // calculate the full namespace path to the final namespace.
  967. size_t loc = urlString.rfind("#");
  968. if (loc != std::string::npos)
  969. {
  970. fileString = urlString.substr(0, loc);
  971. std::string namespacePathString = urlString.substr(loc + 1);
  972. while ((loc = namespacePathString.find("/")) != std::string::npos)
  973. {
  974. namespacePath.push_back(namespacePathString.substr(0, loc));
  975. namespacePathString = namespacePathString.substr(loc + 1);
  976. }
  977. namespacePath.push_back(namespacePathString);
  978. }
  979. else
  980. {
  981. fileString = urlString;
  982. }
  983. }
  984. Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath)
  985. {
  986. // If the url references a specific namespace within the file,
  987. // return the specified namespace or notify the user if it cannot be found.
  988. if (namespacePath.size() > 0)
  989. {
  990. size_t size = namespacePath.size();
  991. properties->rewind();
  992. Properties* iter = properties->getNextNamespace();
  993. for (size_t i = 0; i < size; )
  994. {
  995. while (true)
  996. {
  997. if (iter == NULL)
  998. {
  999. CCLOGWARN("Failed to load properties object from url.");
  1000. return nullptr;
  1001. }
  1002. if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
  1003. {
  1004. if (i != size - 1)
  1005. {
  1006. properties = iter->getNextNamespace();
  1007. iter = properties;
  1008. }
  1009. else
  1010. properties = iter;
  1011. i++;
  1012. break;
  1013. }
  1014. iter = properties->getNextNamespace();
  1015. }
  1016. }
  1017. return properties;
  1018. }
  1019. else
  1020. return properties;
  1021. }
  1022. bool Properties::parseVec2(const char* str, Vec2* out)
  1023. {
  1024. if (str)
  1025. {
  1026. float x, y;
  1027. if (sscanf(str, "%f,%f", &x, &y) == 2)
  1028. {
  1029. if (out)
  1030. out->set(x, y);
  1031. return true;
  1032. }
  1033. else
  1034. {
  1035. CCLOGWARN("Error attempting to parse property as a two-dimensional vector: %s", str);
  1036. }
  1037. }
  1038. if (out)
  1039. out->set(0.0f, 0.0f);
  1040. return false;
  1041. }
  1042. bool Properties::parseVec3(const char* str, Vec3* out)
  1043. {
  1044. if (str)
  1045. {
  1046. float x, y, z;
  1047. if (sscanf(str, "%f,%f,%f", &x, &y, &z) == 3)
  1048. {
  1049. if (out)
  1050. out->set(x, y, z);
  1051. return true;
  1052. }
  1053. else
  1054. {
  1055. CCLOGWARN("Error attempting to parse property as a three-dimensional vector: %s", str);
  1056. }
  1057. }
  1058. if (out)
  1059. out->set(0.0f, 0.0f, 0.0f);
  1060. return false;
  1061. }
  1062. bool Properties::parseVec4(const char* str, Vec4* out)
  1063. {
  1064. if (str)
  1065. {
  1066. float x, y, z, w;
  1067. if (sscanf(str, "%f,%f,%f,%f", &x, &y, &z, &w) == 4)
  1068. {
  1069. if (out)
  1070. out->set(x, y, z, w);
  1071. return true;
  1072. }
  1073. else
  1074. {
  1075. CCLOGWARN("Error attempting to parse property as a four-dimensional vector: %s", str);
  1076. }
  1077. }
  1078. if (out)
  1079. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  1080. return false;
  1081. }
  1082. bool Properties::parseAxisAngle(const char* str, Quaternion* out)
  1083. {
  1084. if (str)
  1085. {
  1086. float x, y, z, theta;
  1087. if (sscanf(str, "%f,%f,%f,%f", &x, &y, &z, &theta) == 4)
  1088. {
  1089. if (out)
  1090. out->set(Vec3(x, y, z), MATH_DEG_TO_RAD(theta));
  1091. return true;
  1092. }
  1093. else
  1094. {
  1095. CCLOGWARN("Error attempting to parse property as an axis-angle rotation: %s", str);
  1096. }
  1097. }
  1098. if (out)
  1099. out->set(0.0f, 0.0f, 0.0f, 1.0f);
  1100. return false;
  1101. }
  1102. bool Properties::parseColor(const char* str, Vec3* out)
  1103. {
  1104. if (str)
  1105. {
  1106. if (strlen(str) == 7 && str[0] == '#')
  1107. {
  1108. // Read the string into an int as hex.
  1109. unsigned int color;
  1110. if (sscanf(str + 1, "%x", &color) == 1)
  1111. {
  1112. if (out)
  1113. out->set(Vec3::fromColor(color));
  1114. return true;
  1115. }
  1116. else
  1117. {
  1118. // Invalid format
  1119. CCLOGWARN("Error attempting to parse property as an RGB color: %s", str);
  1120. }
  1121. }
  1122. else
  1123. {
  1124. // Not a color string.
  1125. CCLOGWARN("Error attempting to parse property as an RGB color (not specified as a color string): %s", str);
  1126. }
  1127. }
  1128. if (out)
  1129. out->set(0.0f, 0.0f, 0.0f);
  1130. return false;
  1131. }
  1132. bool Properties::parseColor(const char* str, Vec4* out)
  1133. {
  1134. if (str)
  1135. {
  1136. if (strlen(str) == 9 && str[0] == '#')
  1137. {
  1138. // Read the string into an int as hex.
  1139. unsigned int color;
  1140. if (sscanf(str + 1, "%x", &color) == 1)
  1141. {
  1142. if (out)
  1143. out->set(Vec4::fromColor(color));
  1144. return true;
  1145. }
  1146. else
  1147. {
  1148. // Invalid format
  1149. CCLOGWARN("Error attempting to parse property as an RGBA color: %s", str);
  1150. }
  1151. }
  1152. else
  1153. {
  1154. // Not a color string.
  1155. CCLOGWARN("Error attempting to parse property as an RGBA color (not specified as a color string): %s", str);
  1156. }
  1157. }
  1158. if (out)
  1159. out->set(0.0f, 0.0f, 0.0f, 0.0f);
  1160. return false;
  1161. }