PageRenderTime 42ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/utils/octave/xmltoolbox/octavexml/OctaveXMLWriter.cc

https://github.com/sbigaret/Euclide
C++ | 541 lines | 345 code | 116 blank | 80 comment | 82 complexity | 0f3a86fa9ab77ad21c1ad9d277f143b3 MD5 | raw file
  1. /*
  2. Copyright (C) 2007 Thomas Geiger (tom.geiger@gmail.com)
  3. This file is part of Octave.
  4. Octave is free software; you can redistribute it and/or modify it
  5. under the terms of the GNU General Public License as published by the
  6. Free Software Foundation; either version 2, or (at your option) any
  7. later version.
  8. Octave is distributed in the hope that it will be useful, but WITHOUT
  9. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  11. for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with Octave; see the file COPYING. If not, write to the Free
  14. Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301, USA.
  16. */
  17. #include "OctaveXMLWriter.h"
  18. // some prototypes
  19. static void writeAny(ostringstream& xml, const char* parent, const octave_value& val,
  20. int index, int level = -1);
  21. static void addNamespaces(ostringstream& xml, Octave_map& m);
  22. static void addAttributes(ostringstream& xml, Octave_map& m);
  23. static void addComments(ostringstream& xml, Octave_map& m);
  24. static bool compareOctaveValues(const octave_value a, const octave_value& b);
  25. static string trim(const string& s);
  26. static void writeTypesafe(ostringstream& xml, const char* name,
  27. const octave_value val, int index, bool attrs, int level = 0);
  28. static string indent(int i, bool typesafe);
  29. static void writeTypeInfo(ostringstream& xml, const octave_value val, int index,
  30. const char* name);
  31. static void writeDimensions(ostringstream& xml, dim_vector dv);
  32. // constants
  33. static const int C_OUTPUT_PRECISION = 14; // set precision as used in geodise
  34. static const char* C_EMPTY = "";
  35. static const char* C_ATTR = "ATTRS";
  36. static const char* C_COMMENT = "COMMENTS";
  37. static const char* C_NAME = "NAME";
  38. static const char* C_NAMESPACE = "NAMESPACES";
  39. static const char* C_CONTENT = "TEXT";
  40. static const char* C_OP = "<";
  41. static const char* C_CL = ">";
  42. static const char* C_INDENT = " "; // four spaces
  43. static const char* C_INDENT_TYPESAFE = " "; // two spaces (for geodise compat.)
  44. static const char* C_ROOT = "root";
  45. static const char* C_DUMMY_ELEM = "ROOT";
  46. static const char* C_ORDER = "ORDER";
  47. OctaveXMLWriter::OctaveXMLWriter()
  48. {
  49. format = FMT_ANY;
  50. }
  51. OctaveXMLWriter::~OctaveXMLWriter()
  52. {
  53. }
  54. bool OctaveXMLWriter::writeXML(const octave_value& value, ostringstream& output) {
  55. output << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
  56. // evaluate datatype for each arg (recursive)
  57. switch (format) {
  58. case FMT_ANY:
  59. output << endl;
  60. writeAny(output, C_ROOT, value, 1);
  61. return true;
  62. break;
  63. case FMT_TYPESAFE:
  64. output.precision(C_OUTPUT_PRECISION);
  65. output << endl;
  66. writeTypesafe(output, C_ROOT, value, 1, true);
  67. output << endl;
  68. return true;
  69. break;
  70. default:
  71. break;
  72. }
  73. return false;
  74. }
  75. bool OctaveXMLWriter::writeXML(const octave_value& value, const string& fileName) {
  76. ostringstream output;
  77. // try to create the file
  78. // TODO: some checking needed here
  79. ofstream xml(fileName.c_str(), ios::out);
  80. // now write it
  81. writeXML(value, output);
  82. // pass to file
  83. xml << output.str();
  84. xml.close();
  85. return true;
  86. }
  87. void OctaveXMLWriter::setFormat(XML_FORMAT fmt) {
  88. format = fmt;
  89. }
  90. XML_FORMAT OctaveXMLWriter::getFormat() {
  91. return format;
  92. }
  93. /*
  94. * writeAny considers the special names ATTRIBUTE/CONTENT when constructing XML from an octave value
  95. */
  96. static void writeAny(ostringstream& xml, const char* parent,
  97. const octave_value& val, int index, int level) {
  98. string indentStr = indent(level, false);
  99. if (val.is_map()) {
  100. Octave_map m = val.map_value();
  101. addComments(xml, m);
  102. // XXX: if there's no NAME, extract name from current element
  103. // string (mandatory)
  104. string name = "NO_NAME";
  105. if ( m.contains(C_NAME) ) {
  106. name = m.contents(C_NAME).elem(0,0).string_value();
  107. } else {
  108. cout << "ERROR: any - no NAME attribute specified!" << endl;
  109. return;
  110. }
  111. // okay, name has been extracted
  112. m.del(C_NAME);
  113. // this element was of used at the previous level,
  114. // here its not eeded any more
  115. m.del(C_ORDER);
  116. // lets start with an open tag and the name of the element...
  117. if (name != C_DUMMY_ELEM) {
  118. xml << indentStr << C_OP << name;
  119. }
  120. addNamespaces(xml, m);
  121. addAttributes(xml, m);
  122. bool content = false;
  123. bool empty = true;
  124. // do we have some content within the element?
  125. if (m.contains(C_CONTENT)) {
  126. Cell cell = m.contents(C_CONTENT);
  127. if (cell.elem(0,0).is_string() && cell.elem(0,0).string_value().length() > 0) {
  128. // have end tag for the current element
  129. if (name != C_DUMMY_ELEM) {
  130. xml << C_CL;
  131. }
  132. string text = m.contents(C_CONTENT).elem(0,0).string_value();
  133. if (trim(text).length() > 0) {
  134. xml << text;
  135. empty = false;
  136. content = true;
  137. }
  138. }
  139. m.del(C_CONTENT);
  140. }
  141. // do we have any children left?
  142. if (m.numel() > 0) {
  143. if (!content && name != C_DUMMY_ELEM) {
  144. xml << C_CL;
  145. }
  146. empty = false;
  147. xml << endl;
  148. // well this part is pretty funky....
  149. // first we collect all children (cells) and add them to a list,
  150. // we sort them taking the ORDER tag into account
  151. // so we can restore the order of the elements within
  152. // the XML structure properly
  153. // sort elements by ORDER tag
  154. vector <octave_value> children;
  155. Octave_map::iterator i = m.begin();
  156. int j = 0;
  157. while (i != m.end()) {
  158. // process elements (cells of structs)
  159. Cell cell = m.contents(i);
  160. Cell innerCell = cell.elem(0,0).cell_value();
  161. dim_vector d = innerCell.dims();
  162. for (int k = 0; k < d.numel(); k++) {
  163. children.push_back(innerCell.elem(k));
  164. }
  165. i++;
  166. j++;
  167. }
  168. // have the elements sorted asc by ORDER tag
  169. std::sort(children.begin(), children.end(), compareOctaveValues);
  170. // now step down the recursion and process the elements
  171. for (unsigned int l = 0; l < children.size(); l++) {
  172. writeAny(xml, C_EMPTY, children[l], index, level + 1);
  173. }
  174. } else {
  175. indentStr.clear();
  176. }
  177. // close for empty tag
  178. if (empty) {
  179. if (name != C_DUMMY_ELEM) {
  180. xml << "/>"<< endl;
  181. }
  182. return;
  183. }
  184. // close for normal tag
  185. if (name != C_DUMMY_ELEM) {
  186. xml << indentStr << "</"<< name << C_CL << endl;
  187. }
  188. }
  189. }
  190. /*
  191. * have a comparator in order to sort the values by ORDER
  192. */
  193. static bool compareOctaveValues(const octave_value a, const octave_value& b) {
  194. // only to this if both values have an order element,
  195. // otherwise assume that the element with an order is higher than
  196. // an element without
  197. int orderA = 0;
  198. int orderB = 0;
  199. Octave_map amap = a.map_value();
  200. Octave_map bmap = b.map_value();
  201. if (amap.contains(C_ORDER)) {
  202. orderA = amap.contents(C_ORDER).elem(0,0).int_value();
  203. }
  204. if (bmap.contains(C_ORDER)) {
  205. orderB = bmap.contents(C_ORDER).elem(0,0).int_value();
  206. }
  207. return orderA < orderB;
  208. }
  209. static void addNamespaces(ostringstream& xml, Octave_map& m) {
  210. if (m.contains(C_NAMESPACE)) {
  211. // cellarray of map (name, value)
  212. Cell cell = m.contents(C_NAMESPACE);
  213. if ( !m.contents(C_NAMESPACE).elem(0,0).is_string() ) {
  214. Octave_map namespace_map = cell.elem(0,0).map_value();
  215. string_vector key_list = namespace_map.keys();
  216. for (int i = 0; i < key_list.length(); i++) {
  217. xml << " "<< key_list[i]<< "=\""<< namespace_map.contents(key_list[i]).elem(0,0).string_value() << "\"";
  218. }
  219. }
  220. m.del(C_NAMESPACE);
  221. }
  222. }
  223. static void addAttributes(ostringstream& xml, Octave_map& m) {
  224. // look for special fields
  225. if (m.contains(C_ATTR)) {
  226. // cellarray of string
  227. Cell cell = m.contents(C_ATTR);
  228. if ( !cell.elem(0,0).is_string() ) {
  229. Octave_map attr_map = cell.elem(0,0).map_value();
  230. string_vector key_list = attr_map.keys();
  231. for (int i = 0; i < key_list.length(); i++) {
  232. xml << " "<< key_list[i]<< "=\""<< attr_map.contents(key_list[i]).elem(0,0).string_value() << "\"";
  233. }
  234. }
  235. m.del(C_ATTR);
  236. }
  237. }
  238. static void addComments(ostringstream& xml, Octave_map& m) {
  239. if (m.contains(C_COMMENT)) {
  240. // cellarray of string
  241. Cell cell = m.contents(C_COMMENT);
  242. if ( !cell.elem(0,0).is_string() ) {
  243. Cell comments = cell.elem(0,0).cell_value();
  244. dim_vector d = comments.dims();
  245. for (int i = comments.numel() - 1; i >= 0; i--) {
  246. xml << "<!--"<< comments.elem(i).string_value() << "-->"<< endl;
  247. }
  248. }
  249. m.del(C_COMMENT);
  250. }
  251. }
  252. /*
  253. * writeTypesafe converts the octave values to XML which has information for type-safe reconstruction
  254. */
  255. static void writeTypesafe(ostringstream& xml, const char* name,
  256. const octave_value val, int index, bool attrs, int level) {
  257. dim_vector dim_vec = val.dims();
  258. int dimensions = dim_vec.length();
  259. // indentation
  260. string indentStr = indent(level, true);
  261. xml << indentStr;
  262. if (name != C_EMPTY) {
  263. xml << C_OP << name;
  264. }
  265. if (val.is_scalar_type()) {
  266. // find out what kind of scalar -> complex?
  267. if (val.is_complex_type()) {
  268. Complex c = val.complex_value();
  269. xml << " idx=\""<< index << "\" type=\"complex\" size=\"1 1\">";
  270. xml << endl;
  271. xml << indent(level+1, true);
  272. xml << "<item idx=\"1\" type=\"double\" size=\"1 1\">";
  273. xml << c.real() << "</item>";
  274. xml << endl;
  275. xml << indent(level+1, true);
  276. xml << "<item idx=\"2\" type=\"double\" size=\"1 1\">";
  277. xml << c.imag() << "</item>";
  278. xml << endl;
  279. } else if (val.is_numeric_type()) {
  280. indentStr.clear();
  281. writeTypeInfo(xml, val, index, "double");
  282. octave_write_double(xml, val.double_value());
  283. }
  284. } else if (val.is_string()) {
  285. indentStr.clear();
  286. writeTypeInfo(xml, val, index, "char");
  287. if (dimensions > 1) {
  288. charMatrix mtx = val.char_matrix_value();
  289. for (int j = 0; j < dim_vec(0); j++) {
  290. xml << mtx.row_as_string(j);
  291. }
  292. } else {
  293. xml << val.string_value();
  294. }
  295. } else if (val.is_matrix_type()) {
  296. xml << " idx=\""<< index << "\" type=\"";
  297. if (val.is_complex_matrix()) {
  298. ComplexMatrix mtx = val.complex_matrix_value();
  299. xml << "complex\" size=\"" << dim_vec(0) << " " << dim_vec(1) << "\">";
  300. for (int i = 0; i < dim_vec(1); i++) {
  301. xml << endl;
  302. xml << indent(level +1, true);
  303. xml << "<item idx=\""<< i+1
  304. << "\" type=\"double\" size=\"" << dim_vec(0) << " 2\">";
  305. // well, these both loops might be improved some day...
  306. for (int j = 0; j < dim_vec(0); j++) {
  307. Complex c = mtx.elem(j,i);
  308. xml << c.real() << " ";
  309. }
  310. for (int j = 0; j < dim_vec(0); j++) {
  311. Complex c = mtx.elem(j,i);
  312. xml << c.imag() << " ";
  313. }
  314. xml << "</item>";
  315. }
  316. xml << endl;
  317. } else if (val.is_real_matrix()) {
  318. NDArray arr = val.array_value();
  319. indentStr.clear();
  320. xml << "double\"";
  321. writeDimensions(xml, dim_vec);
  322. xml << C_CL;
  323. // this output needs to be generated this way
  324. // to make sure it is printed in the same line
  325. // therefore it is compatible to GEODISE
  326. for (int i = 0; i < dim_vec.numel(); i++) {
  327. xml << arr.elem(i) << " ";
  328. }
  329. } else {
  330. xml << "unknown\"";
  331. }
  332. } else if (val.is_map()) {
  333. // XXX: this section might see some improvements?
  334. Octave_map m = val.map_value();
  335. string_vector key_list = m.keys();
  336. writeTypeInfo(xml, val, index, "struct");
  337. xml << endl;
  338. Octave_map::iterator i = m.begin();
  339. int j = 0;
  340. while (i != m.end()) {
  341. Cell cell = m.contents(i);
  342. dim_vector d = cell.dims();
  343. // is it a real cell or is it a struct?
  344. if (d.length() != 2) {
  345. writeTypesafe (xml, C_EMPTY, cell, index, attrs, level + 1);
  346. } else {
  347. for (int k = 0; k < d.numel(); k++) {
  348. octave_value o_val = cell.elem(k);
  349. writeTypesafe (xml, key_list[j].c_str(), o_val, k+1, attrs,
  350. level + 1);
  351. }
  352. }
  353. i++;
  354. j++;
  355. }
  356. } else if (val.is_cell()) {
  357. dim_vector dv = val.dims();
  358. // somehow a cellarray is a bit different and does not want to be printed
  359. // using writeTypeInfo()
  360. xml << " idx=\""<< index << "\" type=\"cell\" size=\""<< dv(0)
  361. << " "<< dv(1) << "\">";
  362. xml << endl;
  363. octave_cell cell = val.cell_value();
  364. //int elems = 1;
  365. for (int k = 0; k < dv.numel(); k++) {
  366. writeTypesafe (xml, "item", cell.cell_value().elem(k), k+1, attrs, level+1);
  367. }
  368. } else {
  369. // we should never cross this place
  370. error("unknown type");
  371. }
  372. if (name != C_EMPTY) {
  373. xml << indentStr;
  374. xml << "</"<< name << C_CL << endl;
  375. }
  376. }
  377. /*
  378. * helper method - generates an indentation string according to the level passed
  379. */
  380. static string indent(int i, bool typesafe) {
  381. string str;
  382. string in;
  383. if (typesafe) {
  384. in = C_INDENT_TYPESAFE;
  385. } else {
  386. in = C_INDENT;
  387. }
  388. for (int x = 0; x < i; x++) {
  389. str += in;
  390. }
  391. return str;
  392. }
  393. static void writeTypeInfo(ostringstream& xml, octave_value val, int index,
  394. const char* name) {
  395. xml << " idx=\""<< index << "\" type=\""<< name << "\"";
  396. writeDimensions(xml, val.dims());
  397. xml << ">";
  398. }
  399. static void writeDimensions(ostringstream& xml, dim_vector dv) {
  400. int i;
  401. xml << " size=\"";
  402. for (i = 0; i < dv.length() - 1; i++) {
  403. xml << dv(i) << " ";
  404. }
  405. xml << dv(i++) << "\"";
  406. return;
  407. }
  408. static string trim(const string& s) {
  409. if (s.length() == 0)
  410. return s;
  411. int b = s.find_first_not_of(" \t\n");
  412. int e = s.find_last_not_of(" \t\n");
  413. if (b == -1) // No non-spaces
  414. return C_EMPTY;
  415. if (b == 0&& e ==0) { // Empty string
  416. return C_EMPTY;
  417. }
  418. return string(s, b, e - b + 1);
  419. }