properties.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /******************************************************************************
  2. *
  3. * package: Log4Qt
  4. * file: properties.cpp
  5. * created: September 2007
  6. * author: Martin Heinrich
  7. *
  8. *
  9. * Copyright 2007 Martin Heinrich
  10. *
  11. * Licensed under the Apache License, Version 2.0 (the "License");
  12. * you may not use this file except in compliance with the License.
  13. * You may obtain a copy of the License at
  14. *
  15. * http://www.apache.org/licenses/LICENSE-2.0
  16. *
  17. * Unless required by applicable law or agreed to in writing, software
  18. * distributed under the License is distributed on an "AS IS" BASIS,
  19. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  20. * See the License for the specific language governing permissions and
  21. * limitations under the License.
  22. *
  23. ******************************************************************************/
  24. /******************************************************************************
  25. * Dependencies
  26. ******************************************************************************/
  27. #include "log4qt/helpers/properties.h"
  28. #include <QtCore/QDebug>
  29. #include <QtCore/QIODevice>
  30. #include <QtCore/QSettings>
  31. #include <QtCore/QTextStream>
  32. #include "log4qt/logger.h"
  33. namespace Log4Qt
  34. {
  35. /**************************************************************************
  36. *Declarations
  37. **************************************************************************/
  38. /**************************************************************************
  39. * C helper functions
  40. **************************************************************************/
  41. LOG4QT_DECLARE_STATIC_LOGGER(logger, Log4Qt::Properties)
  42. /**************************************************************************
  43. * Class implementation: Properties
  44. **************************************************************************/
  45. void Properties::load(QIODevice *pDevice)
  46. {
  47. const QLatin1Char append_char(msEscapeChar);
  48. if (!pDevice)
  49. {
  50. logger()->warn("No device specified for load.");
  51. return;
  52. }
  53. QTextStream stream(pDevice);
  54. QString line;
  55. int line_number = 0;
  56. QString property;
  57. int property_start_line = 1;
  58. do {
  59. line = trimLeft(stream.readLine());
  60. line_number++;
  61. if (!line.isEmpty() && line.at(line.length() - 1) == append_char)
  62. property += line.left(line.length() - 1);
  63. else
  64. {
  65. property += line;
  66. parseProperty(property, property_start_line);
  67. property.clear();
  68. property_start_line = line_number + 1;
  69. }
  70. }
  71. while (!line.isNull());
  72. }
  73. void Properties::load(const QSettings &rSettings)
  74. {
  75. QStringList keys = rSettings.childKeys();
  76. QString key;
  77. Q_FOREACH(key, keys)
  78. insert(key, rSettings.value(key).toString());
  79. }
  80. QString Properties::property(const QString &rKey) const
  81. {
  82. // Null string indicates the property does not contain the key.
  83. if (contains(rKey))
  84. {
  85. QString value = this->value(rKey);
  86. if (value.isNull())
  87. return QString(QLatin1String(""));
  88. else
  89. return value;
  90. }
  91. if (mpDefaultProperties)
  92. return mpDefaultProperties->property(rKey);
  93. else
  94. return QString();
  95. }
  96. QString Properties::property(const QString &rKey,
  97. const QString &rDefaultValue) const
  98. {
  99. QString value = property(rKey);
  100. if (value.isNull())
  101. return rDefaultValue;
  102. else
  103. return value;
  104. }
  105. QStringList Properties::propertyNames() const
  106. {
  107. QStringList default_keys;
  108. if (mpDefaultProperties)
  109. default_keys = mpDefaultProperties->propertyNames();
  110. QStringList keys = this->keys();
  111. QString key;
  112. Q_FOREACH(key, default_keys)
  113. if (!keys.contains(key))
  114. keys << key;
  115. return keys;
  116. }
  117. void Properties::parseProperty(const QString &rProperty,
  118. int line)
  119. {
  120. Q_ASSERT_X(rProperty == trimLeft(rProperty), "parseProperty()", "rProperty has leading spaces");
  121. enum State
  122. {
  123. KEY_STATE,
  124. KEYSPACE_STATE,
  125. SPACEVALUE_STATE,
  126. VALUE_STATE,
  127. KEYESCAPE_STATE,
  128. VALUEESCAPE_STATE,
  129. UNICODEESCAPE_STATE
  130. };
  131. const QString value_escape_codes =QLatin1String(msValueEscapeCodes);
  132. const QString value_escape_chars = QLatin1String(msValueEscapeChars);
  133. Q_ASSERT_X(value_escape_codes.length() == value_escape_chars.length(), "parseProperty()", "Value escape sequence character definition does not map");
  134. const QString key_escape_codes = QLatin1String(msKeyEscapeCodes);
  135. const QString key_escape_chars = QLatin1String(msKeyEscapeChars);
  136. Q_ASSERT_X(key_escape_codes.length() == key_escape_chars.length(), "parseProperty()", "Key escape sequence character definition does not map");
  137. if (rProperty.isEmpty())
  138. return;
  139. int i = 0;
  140. QChar c;
  141. char ch;
  142. State state = KEY_STATE;
  143. QString key;
  144. QString value;
  145. QString *p_string = &key;
  146. uint ucs;
  147. int ucs_digits;
  148. while (i < rProperty.length())
  149. {
  150. // i points to the current character.
  151. // c contains the current character
  152. // ch contains the Latin1 equivalent of the current character
  153. // i is incremented at the end of the loop to consume the character.
  154. // continue is used to change state without consuming the character
  155. c = rProperty.at(i);
  156. ch = c.toLatin1();
  157. switch (state)
  158. {
  159. case KEY_STATE:
  160. if (ch == '!' || ch == '#' )
  161. return;
  162. else if (c.isSpace())
  163. {
  164. p_string = &value;
  165. state = KEYSPACE_STATE;
  166. }
  167. else if (ch == '=' || ch == ':')
  168. {
  169. p_string = &value;
  170. state = SPACEVALUE_STATE;
  171. }
  172. else if (ch == msEscapeChar)
  173. state = KEYESCAPE_STATE;
  174. else
  175. *p_string += c;
  176. break;
  177. case KEYSPACE_STATE:
  178. if (ch == '=' || ch == ':')
  179. state = SPACEVALUE_STATE;
  180. else if (!c.isSpace())
  181. {
  182. *p_string += c;
  183. state = VALUE_STATE;
  184. }
  185. break;
  186. case SPACEVALUE_STATE:
  187. if (!c.isSpace())
  188. {
  189. *p_string += c;
  190. state = VALUE_STATE;
  191. }
  192. break;
  193. case VALUE_STATE:
  194. if (ch == msEscapeChar)
  195. state = VALUEESCAPE_STATE;
  196. else
  197. *p_string += c;
  198. break;
  199. case KEYESCAPE_STATE:
  200. {
  201. int convert = key_escape_codes.indexOf(c);
  202. if (convert >= 0)
  203. *p_string += key_escape_chars.at(convert);
  204. else
  205. {
  206. logger()->warn("Unknown escape sequence '\\%1' in key of property starting at line %2",
  207. QString(c),
  208. line);
  209. *p_string += c;
  210. }
  211. state = KEY_STATE;
  212. break;
  213. }
  214. case VALUEESCAPE_STATE:
  215. {
  216. int convert = value_escape_codes.indexOf(c);
  217. if (convert >= 0)
  218. {
  219. *p_string += value_escape_chars.at(convert);
  220. state = VALUE_STATE;
  221. }
  222. else if (ch == 'u')
  223. {
  224. ucs = 0;
  225. ucs_digits = 0;
  226. state = UNICODEESCAPE_STATE;
  227. }
  228. else
  229. {
  230. logger()->warn("Unknown escape sequence '\\%1' in value of property starting at line %2", QString(c), line);
  231. *p_string += c;
  232. state = VALUE_STATE;
  233. }
  234. break;
  235. }
  236. case UNICODEESCAPE_STATE:
  237. {
  238. int hex = hexDigitValue(c);
  239. if (hex >= 0)
  240. {
  241. ucs = ucs * 16 + hex;
  242. ucs_digits++;
  243. if (ucs_digits == 4 || i == rProperty.length() - 1)
  244. {
  245. *p_string += QChar(ucs);
  246. state = VALUE_STATE;
  247. }
  248. }
  249. else
  250. {
  251. if (ucs_digits > 0)
  252. *p_string += QChar(ucs);
  253. state = VALUE_STATE;
  254. continue;
  255. }
  256. break;
  257. }
  258. default:
  259. Q_ASSERT_X(false, "Properties::parseProperty()", "Unknown state constant");
  260. return;
  261. }
  262. i++;
  263. }
  264. if (key.isEmpty() && !value.isEmpty())
  265. logger()->warn("Found value with no key in property starting at line %1", line);
  266. logger()->trace("Loaded property '%1' : '%2'", key, value);
  267. insert(key, value);
  268. }
  269. int Properties::hexDigitValue(const QChar &rDigit)
  270. {
  271. bool ok;
  272. int result = QString(rDigit).toInt(&ok, 16);
  273. if (!ok)
  274. return -1;
  275. else
  276. return result;
  277. }
  278. QString Properties::trimLeft(const QString &rLine)
  279. {
  280. int i = 0;
  281. while (i < rLine.length() && rLine.at(i).isSpace())
  282. i++;
  283. return rLine.right(rLine.length() - i);
  284. }
  285. const char Properties::msEscapeChar ='\\';
  286. const char *Properties::msValueEscapeCodes = "tnr\\\"\' ";
  287. const char *Properties::msValueEscapeChars = "\t\n\r\\\"\' ";
  288. const char *Properties::msKeyEscapeCodes = " :=";
  289. const char *Properties::msKeyEscapeChars = " :=";
  290. /**************************************************************************
  291. * Implementation: Operators, Helper
  292. **************************************************************************/
  293. #ifndef QT_NO_DEBUG_STREAM
  294. QDebug operator<<(QDebug debug, const Properties &rProperties)
  295. {
  296. debug.nospace() << "Properties("
  297. << "default:" << rProperties.defaultProperties() << " "
  298. << "properties:" << *reinterpret_cast<const QHash <QString, QString > *>(&rProperties)
  299. << ")";
  300. return debug.space();
  301. }
  302. #endif
  303. } // namespace Log4Qt