PropertySpecification.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. * This source file is part of libRocket, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://www.librocket.com
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. *
  26. */
  27. #include "precompiled.h"
  28. #include <Rocket/Core/PropertySpecification.h>
  29. #include "PropertyShorthandDefinition.h"
  30. #include <Rocket/Core/Log.h>
  31. #include <Rocket/Core/PropertyDefinition.h>
  32. #include <Rocket/Core/PropertyDictionary.h>
  33. #include <Rocket/Core/StyleSheetSpecification.h>
  34. namespace Rocket {
  35. namespace Core {
  36. PropertySpecification::PropertySpecification()
  37. {
  38. }
  39. PropertySpecification::~PropertySpecification()
  40. {
  41. for (PropertyMap::iterator iterator = properties.begin(); iterator != properties.end(); ++iterator)
  42. delete (*iterator).second;
  43. for (ShorthandMap::iterator iterator = shorthands.begin(); iterator != shorthands.end(); ++iterator)
  44. delete (*iterator).second;
  45. }
  46. // Registers a property with a new definition.
  47. PropertyDefinition& PropertySpecification::RegisterProperty(const String& property_name, const String& default_value, bool inherited, bool forces_layout)
  48. {
  49. String lower_case_name = property_name.ToLower();
  50. // Create the property and validate the default value.
  51. PropertyDefinition* property_definition = new PropertyDefinition(default_value, inherited, forces_layout);
  52. // Delete any existing property.
  53. PropertyMap::iterator iterator = properties.find(lower_case_name);
  54. if (iterator != properties.end())
  55. delete (*iterator).second;
  56. properties[lower_case_name] = property_definition;
  57. return *property_definition;
  58. }
  59. // Returns a property definition.
  60. const PropertyDefinition* PropertySpecification::GetProperty(const String& property_name) const
  61. {
  62. PropertyMap::const_iterator iterator = properties.find(property_name);
  63. if (iterator == properties.end())
  64. return NULL;
  65. return (*iterator).second;
  66. }
  67. // Fetches a list of the names of all registered property definitions.
  68. void PropertySpecification::GetRegisteredProperties(PropertyNameList& _properties) const
  69. {
  70. for (PropertyMap::const_iterator i = properties.begin(); i != properties.end(); ++i)
  71. _properties.insert((*i).first);
  72. }
  73. // Registers a shorthand property definition.
  74. bool PropertySpecification::RegisterShorthand(const String& shorthand_name, const String& property_names, ShorthandType type)
  75. {
  76. StringList properties;
  77. StringUtilities::ExpandString(properties, property_names.ToLower());
  78. if (properties.empty())
  79. return false;
  80. String lower_case_name = shorthand_name.ToLower();
  81. // Construct the new shorthand definition and resolve its properties.
  82. PropertyShorthandDefinition* property_shorthand = new PropertyShorthandDefinition();
  83. for (size_t i = 0; i < properties.size(); i++)
  84. {
  85. const PropertyDefinition* property = GetProperty(properties[i]);
  86. if (property == NULL)
  87. {
  88. Log::Message(Log::LT_ERROR, "Shorthand property '%s' was registered with invalid property '%s'.", shorthand_name.CString(), properties[i].CString());
  89. delete property_shorthand;
  90. return false;
  91. }
  92. property_shorthand->properties.push_back(PropertyShorthandDefinition::PropertyDefinitionList::value_type(properties[i], property));
  93. }
  94. if (type == AUTO)
  95. {
  96. if (properties.size() == 4 &&
  97. properties[0].Find("-top") != String::npos &&
  98. properties[1].Find("-right") != String::npos &&
  99. properties[2].Find("-bottom") != String::npos &&
  100. properties[3].Find("-left") != String::npos)
  101. property_shorthand->type = BOX;
  102. else
  103. property_shorthand->type = FALL_THROUGH;
  104. }
  105. else
  106. property_shorthand->type = type;
  107. shorthands[lower_case_name] = property_shorthand;
  108. return true;
  109. }
  110. // Returns a shorthand definition.
  111. const PropertyShorthandDefinition* PropertySpecification::GetShorthand(const String& shorthand_name) const
  112. {
  113. ShorthandMap::const_iterator iterator = shorthands.find(shorthand_name);
  114. if (iterator == shorthands.end())
  115. return NULL;
  116. return (*iterator).second;
  117. }
  118. // Parses a property declaration, setting any parsed and validated properties on the given dictionary.
  119. bool PropertySpecification::ParsePropertyDeclaration(PropertyDictionary& dictionary, const String& property_name, const String& property_value, const String& source_file, int source_line_number) const
  120. {
  121. String lower_case_name = property_name.ToLower();
  122. // Attempt to parse as a single property.
  123. const PropertyDefinition* property_definition = GetProperty(lower_case_name);
  124. StringList property_values;
  125. if (!ParsePropertyValues(property_values, property_value, property_definition == NULL) || property_values.size() == 0)
  126. return false;
  127. if (property_definition != NULL)
  128. {
  129. Property new_property;
  130. new_property.source = source_file;
  131. new_property.source_line_number = source_line_number;
  132. if (property_definition->ParseValue(new_property, property_values[0]))
  133. {
  134. dictionary.SetProperty(lower_case_name, new_property);
  135. return true;
  136. }
  137. return false;
  138. }
  139. // Try as a shorthand.
  140. const PropertyShorthandDefinition* shorthand_definition = GetShorthand(lower_case_name);
  141. if (shorthand_definition != NULL)
  142. {
  143. // If this definition is a 'box'-style shorthand (x-top, x-right, x-bottom, x-left, etc) and there are fewer
  144. // than four values
  145. if (shorthand_definition->type == BOX &&
  146. property_values.size() < 4)
  147. {
  148. switch (property_values.size())
  149. {
  150. // Only one value is defined, so it is parsed onto all four sides.
  151. case 1:
  152. {
  153. for (int i = 0; i < 4; i++)
  154. {
  155. Property new_property;
  156. if (!shorthand_definition->properties[i].second->ParseValue(new_property, property_values[0]))
  157. return false;
  158. new_property.source = source_file;
  159. new_property.source_line_number = source_line_number;
  160. dictionary.SetProperty(shorthand_definition->properties[i].first, new_property);
  161. }
  162. }
  163. break;
  164. // Two values are defined, so the first one is parsed onto the top and bottom value, the second onto
  165. // the left and right.
  166. case 2:
  167. {
  168. // Parse the first value into the top and bottom properties.
  169. Property new_property;
  170. new_property.source = source_file;
  171. new_property.source_line_number = source_line_number;
  172. if (!shorthand_definition->properties[0].second->ParseValue(new_property, property_values[0]))
  173. return false;
  174. dictionary.SetProperty(shorthand_definition->properties[0].first, new_property);
  175. if (!shorthand_definition->properties[2].second->ParseValue(new_property, property_values[0]))
  176. return false;
  177. dictionary.SetProperty(shorthand_definition->properties[2].first, new_property);
  178. // Parse the second value into the left and right properties.
  179. if (!shorthand_definition->properties[1].second->ParseValue(new_property, property_values[1]))
  180. return false;
  181. dictionary.SetProperty(shorthand_definition->properties[1].first, new_property);
  182. if (!shorthand_definition->properties[3].second->ParseValue(new_property, property_values[1]))
  183. return false;
  184. dictionary.SetProperty(shorthand_definition->properties[3].first, new_property);
  185. }
  186. break;
  187. // Three values are defined, so the first is parsed into the top value, the second onto the left and
  188. // right, and the third onto the bottom.
  189. case 3:
  190. {
  191. // Parse the first value into the top property.
  192. Property new_property;
  193. new_property.source = source_file;
  194. new_property.source_line_number = source_line_number;
  195. if (!shorthand_definition->properties[0].second->ParseValue(new_property, property_values[0]))
  196. return false;
  197. dictionary.SetProperty(shorthand_definition->properties[0].first, new_property);
  198. // Parse the second value into the left and right properties.
  199. if (!shorthand_definition->properties[1].second->ParseValue(new_property, property_values[1]))
  200. return false;
  201. dictionary.SetProperty(shorthand_definition->properties[1].first, new_property);
  202. if (!shorthand_definition->properties[3].second->ParseValue(new_property, property_values[1]))
  203. return false;
  204. dictionary.SetProperty(shorthand_definition->properties[3].first, new_property);
  205. // Parse the third value into the bottom property.
  206. if (!shorthand_definition->properties[2].second->ParseValue(new_property, property_values[2]))
  207. return false;
  208. dictionary.SetProperty(shorthand_definition->properties[2].first, new_property);
  209. }
  210. break;
  211. default: break;
  212. }
  213. }
  214. else
  215. {
  216. size_t value_index = 0;
  217. size_t property_index = 0;
  218. for (; value_index < property_values.size() && property_index < shorthand_definition->properties.size(); property_index++)
  219. {
  220. Property new_property;
  221. new_property.source = source_file;
  222. new_property.source_line_number = source_line_number;
  223. if (!shorthand_definition->properties[property_index].second->ParseValue(new_property, property_values[value_index]))
  224. {
  225. // This definition failed to parse; if we're falling through, try the next property. If there is no
  226. // next property, then abort!
  227. if (shorthand_definition->type == FALL_THROUGH)
  228. {
  229. if (property_index + 1 < shorthand_definition->properties.size())
  230. continue;
  231. }
  232. return false;
  233. }
  234. dictionary.SetProperty(shorthand_definition->properties[property_index].first, new_property);
  235. // Increment the value index, unless we're replicating the last value and we're up to the last value.
  236. if (shorthand_definition->type != REPLICATE ||
  237. value_index < property_values.size() - 1)
  238. value_index++;
  239. }
  240. }
  241. return true;
  242. }
  243. // Can't find it! Store as an unknown string value.
  244. Property new_property(property_value, Property::UNKNOWN);
  245. new_property.source = source_file;
  246. new_property.source_line_number = source_line_number;
  247. dictionary.SetProperty(lower_case_name, new_property);
  248. return true;
  249. }
  250. // Sets all undefined properties in the dictionary to their defaults.
  251. void PropertySpecification::SetPropertyDefaults(PropertyDictionary& dictionary) const
  252. {
  253. for (PropertyMap::const_iterator i = properties.begin(); i != properties.end(); ++i)
  254. {
  255. if (dictionary.GetProperty((*i).first) == NULL)
  256. dictionary.SetProperty((*i).first, *(*i).second->GetDefaultValue());
  257. }
  258. }
  259. bool PropertySpecification::ParsePropertyValues(StringList& values_list, const String& values, bool split_values) const
  260. {
  261. String value;
  262. enum ParseState { VALUE, VALUE_PARENTHESIS, VALUE_QUOTE };
  263. ParseState state = VALUE;
  264. int open_parentheses = 0;
  265. size_t character_index = 0;
  266. char previous_character = 0;
  267. while (character_index < values.Length())
  268. {
  269. char character = values[character_index];
  270. character_index++;
  271. switch (state)
  272. {
  273. case VALUE:
  274. {
  275. if (character == ';')
  276. {
  277. value = StringUtilities::StripWhitespace(value);
  278. if (value.Length() > 0)
  279. {
  280. values_list.push_back(value);
  281. value.Clear();
  282. }
  283. }
  284. else if (StringUtilities::IsWhitespace(character))
  285. {
  286. if (split_values)
  287. {
  288. value = StringUtilities::StripWhitespace(value);
  289. if (value.Length() > 0)
  290. {
  291. values_list.push_back(value);
  292. value.Clear();
  293. }
  294. }
  295. else
  296. value.Append(character);
  297. }
  298. else if (character == '"')
  299. {
  300. if (split_values)
  301. {
  302. value = StringUtilities::StripWhitespace(value);
  303. if (value.Length() > 0)
  304. {
  305. values_list.push_back(value);
  306. value.Clear();
  307. }
  308. state = VALUE_QUOTE;
  309. }
  310. else
  311. {
  312. value.Append(' ');
  313. state = VALUE_QUOTE;
  314. }
  315. }
  316. else if (character == '(')
  317. {
  318. open_parentheses = 1;
  319. value.Append(character);
  320. state = VALUE_PARENTHESIS;
  321. }
  322. else
  323. {
  324. value.Append(character);
  325. }
  326. }
  327. break;
  328. case VALUE_PARENTHESIS:
  329. {
  330. if (previous_character == '/')
  331. {
  332. if (character == ')' || character == '(')
  333. value.Append(character);
  334. else
  335. {
  336. value.Append('/');
  337. value.Append(character);
  338. }
  339. }
  340. else
  341. {
  342. if (character == '(')
  343. {
  344. open_parentheses++;
  345. value.Append(character);
  346. }
  347. else if (character == ')')
  348. {
  349. open_parentheses--;
  350. value.Append(character);
  351. if (open_parentheses == 0)
  352. state = VALUE;
  353. }
  354. else if (character != '/')
  355. {
  356. value.Append(character);
  357. }
  358. }
  359. }
  360. break;
  361. case VALUE_QUOTE:
  362. {
  363. if (previous_character == '/')
  364. {
  365. if (character == '"')
  366. value.Append(character);
  367. else
  368. {
  369. value.Append('/');
  370. value.Append(character);
  371. }
  372. }
  373. else
  374. {
  375. if (character == '"')
  376. {
  377. if (split_values)
  378. {
  379. value = StringUtilities::StripWhitespace(value);
  380. if (value.Length() > 0)
  381. {
  382. values_list.push_back(value);
  383. value.Clear();
  384. }
  385. }
  386. else
  387. value.Append(' ');
  388. state = VALUE;
  389. }
  390. else if (character != '/')
  391. {
  392. value.Append(character);
  393. }
  394. }
  395. }
  396. }
  397. previous_character = character;
  398. }
  399. if (state == VALUE)
  400. {
  401. value = StringUtilities::StripWhitespace(value);
  402. if (value.Length() > 0)
  403. values_list.push_back(value);
  404. }
  405. return true;
  406. }
  407. }
  408. }