00001 module xmlstorage;
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 import std.stdio;
00042 import std.ctype;
00043 import std.string;
00044 import std.stream;
00045 import std.cstream;
00046 import std.conv;
00047
00048
00049 const BUFFER_LEN = 2048;
00050
00051 const char[] XS_TRUE = "true";
00052 const char[] XS_FALSE = "false";
00053
00054 const char[] XS_INTFMT = "%d";
00055 const char[] XS_FLOATFMT = "%d";
00056
00057 const char[] XS_EMPTY = "";
00058
00059
00060 int isxmlsym(byte c)
00061 {
00062 return isalnum(c) || c=='_' || c=='-';
00063 }
00064
00065
00066
00067 class tifstream : public BufferedFile
00068 {
00069 public:
00070 this(in char[] path)
00071 {
00072 super(path, FileMode.In);
00073 }
00074 };
00075
00076
00077 class tofstream : public BufferedFile
00078 {
00079 public:
00080 this(in char[] path)
00081 {
00082 super(path, FileMode.Out);
00083 }
00084
00085 ~this()
00086 {
00087 close();
00088 }
00089 };
00090
00091
00092 class list(T)
00093 {
00094 public:
00095 void push_back(T entry)
00096 {
00097 _entries.length = _entries.length + 1;
00098 _entries[_entries.length-1] = entry;
00099 }
00100
00101 T front()
00102 {
00103 assert(_entries.length>0);
00104 return _entries[0];
00105 }
00106
00107 T back()
00108 {
00109 assert(_entries.length>0);
00110 return _entries[_entries.length-1];
00111 }
00112
00113 bool empty()
00114 {
00115 return !_entries.length;
00116 }
00117
00118 int length()
00119 {
00120 return _entries.length;
00121 }
00122
00123 void clear()
00124 {
00125 foreach(entry; _entries)
00126 delete entry;
00127
00128 _entries = null;
00129 }
00130
00131 int opApply(int delegate(inout T) dg)
00132 {
00133 int result = 0;
00134
00135 foreach(e; _entries) {
00136 result = dg(e);
00137
00138 if (result)
00139 break;
00140 }
00141
00142 return result;
00143 }
00144
00145 protected:
00146 T[] _entries;
00147 };
00148
00149
00150 class stack(T)
00151 {
00152 public:
00153 void push(T entry)
00154 {
00155 _entries.length = _entries.length + 1;
00156 _entries[_entries.length-1] = entry;
00157 }
00158
00159 T pop()
00160 {
00161 assert(_entries.length>0);
00162 T ret = _entries[_entries.length-1];
00163
00164 _entries.length = _entries.length - 1;
00165
00166 return ret;
00167 }
00168
00169 T top()
00170 {
00171 assert(_entries.length>0);
00172 return _entries[_entries.length-1];
00173 }
00174
00175 bool empty()
00176 {
00177 return !_entries.length;
00178 }
00179
00180 int length()
00181 {
00182 return _entries.length;
00183 }
00184
00185 void clear()
00186 {
00187 foreach(entry; _entries)
00188 delete entry;
00189
00190 _entries = null;
00191 }
00192
00193 protected:
00194 T[] _entries;
00195 };
00196
00197
00198
00199 const char[] XML_INDENT_SPACE = " ";
00200
00201
00202
00203 class XMLError
00204 {
00205 public:
00206 this()
00207 {
00208 _line = 0;
00209 _column = 0;
00210 _error_code = 0;
00211 }
00212
00213
00214 char[] str()
00215 {
00216 return format("%s(%d) [column %d] : %s", _systemId, _line, _column, _message);
00217 }
00218
00219 Stream output(Stream o)
00220 {
00221 o.writeString(str());
00222
00223 return o;
00224 }
00225
00226 char[] _message;
00227 char[] _systemId;
00228 int _line;
00229 int _column;
00230 int _error_code;
00231 };
00232
00233
00234 class XMLErrorList : public list!(XMLError)
00235 {
00236 public:
00237
00238 char[] str()
00239 {
00240 Stream o = new MemoryStream;
00241
00242 foreach(e; _entries)
00243 o.writeString(e.str() ~ "\n");
00244
00245 return o.toString();
00246 }
00247 };
00248
00249
00250 version(XMLNODE_LOCATION) {
00251
00252
00253 class XMLLocation
00254 {
00255 this()
00256 {
00257 _display_path = null;
00258 _line = 0;
00259 _column = 0;
00260 }
00261
00262 this(in char[] display_path, int line, int column)
00263 {
00264 _display_path = display_path;
00265 _line = line;
00266 _column = column;
00267 }
00268
00269 char[] str()
00270 {
00271 return "";
00272 }
00273
00274 protected:
00275 char[] _display_path;
00276 int _line;
00277 int _column;
00278 };
00279
00280 }
00281
00282
00283 enum PRETTY_FLAGS {
00284 PRETTY_PLAIN = 0,
00285 PRETTY_LINEFEED = 1,
00286 PRETTY_INDENT = 2
00287 };
00288
00289
00290
00291 class StyleSheet
00292 {
00293 public:
00294
00295 char[] _href;
00296 char[] _type;
00297 char[] _title;
00298 char[] _media;
00299 char[] _charset;
00300 bool _alternate;
00301
00302 this()
00303 {
00304 _alternate = false;
00305 }
00306
00307 this(in char[] href, in char[] type="text/xsl", bool alternate=false)
00308 {
00309 _href = href;
00310 _type = type;
00311 _alternate = alternate;
00312 }
00313
00314 bool empty()
00315 {
00316 return !_href.length;
00317 }
00318
00319 void print(Stream o)
00320 {
00321 o.writeString("<?xml-stylesheet" ~
00322 " href=\"" ~ _href ~ "\""
00323 " type=\"" ~ _type ~ "\"");
00324
00325 if (_title.length)
00326 o.writeString(" title=\"" ~ _title ~ "\"");
00327
00328 if (_media.length)
00329 o.writeString(" media=\"" ~ _media ~ "\"");
00330
00331 if (_charset.length)
00332 o.writeString(" charset=\"" ~ _charset ~ "\"");
00333
00334 if (_alternate)
00335 o.writeString(" alternate=\"yes\"");
00336
00337 o.writeString("?>");
00338 }
00339 };
00340
00341
00342 class StyleSheetList : public list!(StyleSheet)
00343 {
00344 public:
00345
00346 void set(StyleSheet stylesheet)
00347 {
00348 clear();
00349 push_back(stylesheet);
00350 }
00351 };
00352
00353
00354
00355 class DocType
00356 {
00357 char[] _name;
00358
00359
00360 char[] _public;
00361 char[] _system;
00362
00363
00364
00365 void parse(in char[] str)
00366 {
00367 int i = 0;
00368
00369 while(i<str.length && isspace(cast(dchar)str[i])) ++i;
00370
00371 int start = i;
00372 while(i<str.length && isxmlsym(str[i])) ++i;
00373 _name = str[start..i];
00374
00375 while(i<str.length && isspace(cast(dchar)str[i])) ++i;
00376
00377 start = i;
00378 while(i<str.length && isxmlsym(str[i])) ++i;
00379
00380 char[] keyword = str[start..i];
00381
00382 while(i<str.length && isspace(cast(dchar)str[i])) ++i;
00383
00384 if (i<str.length && str[i]=='"' || str[i]=='\'') {
00385 char delim = str[i];
00386
00387 start = ++i;
00388 while(i<str.length && str[i]!=delim) ++i;
00389
00390 if (str[i] == delim)
00391 _public = str[start..i++];
00392 } else
00393 _public.length = 0;
00394
00395 while(i<str.length && isspace(cast(dchar)str[i])) ++i;
00396
00397 if (i<str.length && str[i]=='"' || str[i]=='\'') {
00398 char delim = str[i];
00399
00400 start = ++i;
00401 while(i<str.length && str[i]!=delim) ++i;
00402
00403 if (str[i] == delim)
00404 _system = str[start..i++];
00405 } else
00406 _system.length = 0;
00407 }
00408
00409 bool empty() {return !_name.length;}
00410 };
00411
00412
00413 class XMLFormat
00414 {
00415 public:
00416 this(PRETTY_FLAGS pretty=PRETTY_FLAGS.PRETTY_INDENT, in char[] xml_version="1.0", in char[] encoding="utf-8", in DocType doctype=new DocType)
00417 {
00418 _pretty = pretty;
00419 _endl = "\n";
00420 _version = xml_version;
00421 _encoding = encoding;
00422 _doctype = doctype;
00423 _standalone = -1;
00424
00425 _stylesheets = new StyleSheetList;
00426 }
00427
00428 void print_header(Stream o, bool lf=true)
00429 {
00430 o.writeString("<?xml version=\"" ~ _version ~ "\" encoding=\"" ~ _encoding ~ "\"");
00431
00432 if (_standalone != -1)
00433 o.writeString(" standalone=\"yes\"");
00434
00435 o.writeString("?>");
00436
00437 if (lf)
00438 o.writeString(_endl);
00439
00440 if (!_doctype.empty()) {
00441 o.writeString("<!DOCTYPE " ~ _doctype._name);
00442
00443 if (_doctype._public.length) {
00444 o.writeString(" PUBLIC \"" ~ _doctype._public ~ '"');
00445
00446 if (lf)
00447 o.writeString(_endl);
00448
00449 o.writeString(" \"" ~ _doctype._system ~ '"');
00450 } else if (_doctype._system.length)
00451 o.writeString(" SYSTEM \"" ~ _doctype._system ~ '"');
00452
00453 o.writeString("?>");
00454
00455 if (lf)
00456 o.writeString(_endl);
00457 }
00458
00459 foreach(it; _stylesheets) {
00460 it.print(o);
00461
00462 if (lf)
00463 o.writeString(_endl);
00464 }
00465
00466
00467
00468
00469
00470
00471
00472 }
00473
00474 PRETTY_FLAGS _pretty;
00475 char[] _endl;
00476
00477 char[] _version;
00478 char[] _encoding;
00479
00480 DocType _doctype;
00481
00482 StyleSheetList _stylesheets;
00483
00484
00485
00486 int _standalone;
00487 };
00488
00489
00490 enum WRITE_MODE {
00491 FORMAT_PLAIN,
00492 FORMAT_SMART,
00493 FORMAT_ORIGINAL,
00494 FORMAT_PRETTY
00495 };
00496
00497
00498
00499 class XMLNode
00500 {
00501 public:
00502
00503 char[] _name;
00504
00505
00506 public typedef char[][char[]] AttributeMap;
00507
00508
00509 class Children : public list!(XMLNode)
00510 {
00511 public:
00512 void assign(Children other)
00513 {
00514 clear();
00515
00516 foreach(node; other)
00517 push_back(new XMLNode(node));
00518 }
00519 };
00520
00521 this(in char[] name)
00522 {
00523 _name = name;
00524
00525 _children = new Children;
00526 }
00527
00528 this(in char[] name, char[] leading)
00529 {
00530 this(name);
00531
00532 _leading = leading;
00533 }
00534
00535 this(XMLNode other)
00536 {
00537 this(other._name);
00538
00539 _attributes = other._attributes;
00540 _leading = other._leading;
00541 _content = other._content;
00542 _end_leading = other._end_leading;
00543 _trailing = other._trailing;
00544
00545 version(XMLNODE_LOCATION) {
00546 _location = other._location;
00547 }
00548
00549 foreach(node; other._children)
00550 _children.push_back(new XMLNode(node));
00551 }
00552
00553
00554
00555
00556
00557
00558
00559 void clear()
00560 {
00561 _leading.length = 0;
00562 _content.length = 0;
00563 _end_leading.length = 0;
00564 _trailing.length = 0;
00565
00566 _attributes = null;
00567 _children.clear();
00568
00569 _name.length = 0;
00570 }
00571
00572 XMLNode assign(XMLNode other)
00573 {
00574 _children.assign(other._children);
00575
00576 _attributes = other._attributes;
00577
00578 _leading = other._leading;
00579 _content = other._content;
00580 _end_leading = other._end_leading;
00581 _trailing = other._trailing;
00582
00583 return this;
00584 }
00585
00586
00587 void add_child(XMLNode child)
00588 {
00589 _children.push_back(child);
00590 }
00591
00592
00593 void put(in char[] attr_name, in char[] value)
00594 {
00595 _attributes[attr_name] = value;
00596 }
00597
00598
00599 char[] opIndex(in char[] attr_name)
00600 {
00601 return _attributes[attr_name];
00602 }
00603
00604
00605 void opIndexAssign(in char[] attr_name, in char[] value)
00606 {
00607 _attributes[attr_name] = value;
00608 }
00609
00610
00611 char[] get(in char[] attr_name, in char[] def="")
00612 {
00613 char[]* found = attr_name in _attributes;
00614
00615 if (found)
00616 return *found;
00617 else
00618 return def;
00619 }
00620
00621
00622 char[]* get_ref(in char[] attr_name, in char[] def="")
00623 {
00624 char[]* found = attr_name in _attributes;
00625
00626 if (found)
00627 return found;
00628 else {
00629 _attributes[attr_name] = def;
00630 return &_attributes[attr_name];
00631 }
00632 }
00633
00634
00635 char[] subvalue(in char[] name, char[] attr_name, int n=0)
00636 {
00637 XMLNode node = find(name, n);
00638
00639 if (node)
00640 return node.get(attr_name);
00641 else
00642 return "";
00643 }
00644
00645
00646 char[]* subvalue_ref(in char[] name, char[] attr_name, int n=0)
00647 {
00648 XMLNode node = find(name, n);
00649
00650 if (node)
00651 return node.get_ref(attr_name);
00652 else {
00653 _attributes[attr_name] = "";
00654 return &_attributes[attr_name];
00655 }
00656 }
00657
00658
00659 char[] put_subvalue(in char[] name, char[] attr_name, int n=0)
00660 {
00661 XMLNode node = find(name, n);
00662
00663 if (!node) {
00664 node = new XMLNode(name);
00665 add_child(node);
00666 }
00667
00668 return node.get(attr_name);
00669 }
00670
00671 Children get_children()
00672 {
00673 return _children;
00674 }
00675
00676 AttributeMap get_attributes()
00677 {
00678 return _attributes;
00679 }
00680
00681 char[] get_content()
00682 {
00683 return DecodeXMLString(_content);
00684 }
00685
00686 void set_content(in char[] s, bool cdata=false)
00687 {
00688 _content = EncodeXMLString(s, cdata);
00689 }
00690
00691 version(XMLNODE_LOCATION) {
00692 XMLLocation get_location() {return _location;}
00693 }
00694
00695
00696 void write(Stream o, XMLFormat format, WRITE_MODE mode=WRITE_MODE.FORMAT_SMART, int indent=0)
00697 {
00698 switch(mode) {
00699 case WRITE_MODE.FORMAT_PLAIN:
00700 plain_write_worker(o);
00701 break;
00702
00703 case WRITE_MODE.FORMAT_PRETTY:
00704 pretty_write_worker(o, format, indent);
00705 break;
00706
00707 case WRITE_MODE.FORMAT_ORIGINAL:
00708 write_worker(o);
00709 break;
00710
00711 default:
00712 smart_write_worker(o, format, indent);
00713 }
00714 }
00715
00716 protected:
00717 Children _children;
00718 AttributeMap _attributes;
00719
00720 char[] _leading;
00721 char[] _content;
00722 char[] _end_leading;
00723 char[] _trailing;
00724
00725 version(XMLNODE_LOCATION) {
00726 XMLLocation _location;
00727 }
00728
00729 XMLNode get_first_child()
00730 {
00731 if (!_children.empty())
00732 return _children.front();
00733 else
00734 return null;
00735 }
00736
00737 XMLNode find(in char[] name, int n=0)
00738 {
00739 foreach(node; _children)
00740 if (node._name == name)
00741 if (!n--)
00742 return node;
00743
00744 return null;
00745 }
00746
00747 XMLNode find(in char[] name, char[] attr_name, char[] attr_value, int n=0)
00748 {
00749 foreach(node; _children)
00750 if (node._name==name && node.get(attr_name)==attr_value)
00751 if (!n--)
00752 return node;
00753
00754 return null;
00755 }
00756
00757
00758 XMLNode find_relative(in char[] path)
00759 {
00760 XMLNode node = this;
00761
00762
00763 while(path.length) {
00764 node = node.get_child_relative(path, false);
00765
00766 if (!node)
00767 return null;
00768
00769 if (*path == '/')
00770 path = path[1..length];
00771 }
00772
00773 return node;
00774 }
00775
00776
00777 XMLNode create_relative(in char[] path)
00778 {
00779 XMLNode node = this;
00780
00781
00782 while(path.length) {
00783 node = node.get_child_relative(path, true);
00784
00785 if (*path == '/')
00786 path = path[1..length];
00787 }
00788
00789 return node;
00790 }
00791
00792
00793 void write_worker(Stream o)
00794 {
00795 o.writeString(_leading ~ '<' ~ EncodeXMLString(_name));
00796
00797 foreach(key,value; _attributes)
00798 o.writeString(' ' ~ EncodeXMLString(key) ~ "=\"" ~ EncodeXMLString(value) ~ "\"");
00799
00800 if (!_children.empty() || _content.length) {
00801 o.writeString('>' ~ _content);
00802
00803 foreach(child; _children)
00804 child.write_worker(o);
00805
00806 o.writeString(_end_leading ~ "</" ~ EncodeXMLString(_name) ~ '>');
00807 } else
00808 o.writeString("/>");
00809
00810 o.writeString(_trailing);
00811 }
00812
00813
00814 void plain_write_worker(Stream o)
00815 {
00816 o.writeString('<' ~ EncodeXMLString(_name));
00817
00818 foreach(key,value; _attributes)
00819 o.writeString(' ' ~ EncodeXMLString(key) ~ "=\"" ~ EncodeXMLString(value) ~ "\"");
00820
00821
00822 char[] content = _content;
00823 while(content.length && isspace(cast(dchar)*content)) content = content[1 .. length];
00824
00825 if (!_children.empty() || content.length) {
00826 o.writeString('>' ~ content);
00827
00828 foreach(child; _children)
00829 child.plain_write_worker(o);
00830
00831 o.writeString("</" ~ EncodeXMLString(_name) ~ ">");
00832 } else
00833 o.writeString("/>");
00834 }
00835
00836
00837 void pretty_write_worker(Stream o, XMLFormat format, int indent)
00838 {
00839 for(int i=indent; i--; )
00840 o.writeString(XML_INDENT_SPACE);
00841
00842 o.writeString('<' ~ EncodeXMLString(_name));
00843
00844 foreach(key,value; _attributes)
00845 o.writeString(' ' ~ EncodeXMLString(key) ~ "=\"" ~ EncodeXMLString(value) ~ "\"");
00846
00847
00848 char[] content = _content;
00849 while(content.length && isspace(cast(dchar)*content)) content = content[1 .. length];
00850
00851 if (!_children.empty() || content.length) {
00852 o.writeString('>' ~ content);
00853
00854 if (!_children.empty())
00855 o.writeString(format._endl);
00856
00857 foreach(child; _children)
00858 child.pretty_write_worker(o, format, indent+1);
00859
00860 for(int i=indent; i--; )
00861 o.writeString(XML_INDENT_SPACE);
00862
00863 o.writeString("</" ~ EncodeXMLString(_name) ~ '>' ~ format._endl);
00864 } else
00865 o.writeString("/>" ~ format._endl);
00866 }
00867
00868
00869 void smart_write_worker(Stream o, XMLFormat format, int indent)
00870 {
00871
00872 char[] leading = _leading;
00873 while(leading.length && *leading=='\n')
00874 leading = leading[1 .. length];
00875
00876 if (!leading.length)
00877 for(int i=indent; i--; )
00878 o.writeString(XML_INDENT_SPACE);
00879 else
00880 o.writeString(leading);
00881
00882 o.writeString('<' ~ EncodeXMLString(_name));
00883
00884 foreach(key,value; _attributes)
00885 o.writeString(' ' ~ EncodeXMLString(key) ~ "=\"" ~ EncodeXMLString(value) ~ "\"");
00886
00887
00888 char[] content = _content;
00889 while(content.length && isspace(cast(dchar)*content)) content = content[1 .. length];
00890
00891 if (_children.empty() && !content.length)
00892 o.writeString("/>");
00893 else {
00894 o.writeString(">");
00895
00896 if (!content.length)
00897 o.writeString(format._endl);
00898 else
00899 o.writeString(content);
00900
00901 if (!_children.empty()) {
00902 foreach(child; _children)
00903 child.smart_write_worker(o, format, indent+1);
00904
00905
00906 char[] end_leading = _end_leading;
00907 while(end_leading.length && *end_leading=='\n')
00908 end_leading = end_leading[1 .. length];
00909
00910 if (!end_leading.length)
00911 for(int i=indent; i--; )
00912 o.writeString(XML_INDENT_SPACE);
00913 else
00914 o.writeString(end_leading);
00915 } else
00916 o.writeString(_end_leading);
00917
00918 o.writeString("</" ~ EncodeXMLString(_name) ~ '>');
00919 }
00920
00921 if (!_trailing.length)
00922 o.writeString(format._endl);
00923 else
00924 o.writeString(_trailing);
00925 }
00926
00927 protected:
00928 XMLNode get_child_relative(inout char[] path, bool create)
00929 {
00930 int slash_pos = .find(path, '/');
00931 if (slash_pos == 0)
00932 return null;
00933
00934 size_t l = slash_pos!=-1? slash_pos: path.length;
00935 char[] comp = path[0 .. l];
00936 path = path[l .. length];
00937
00938
00939 int bracket_pos = .find(comp, '[');
00940 l = bracket_pos!=-1? bracket_pos: comp.length;
00941
00942 char[] child_name = comp[0 .. l];
00943
00944 char[] attr_name, attr_value;
00945 int n = 0;
00946
00947 if (bracket_pos != -1) {
00948 char[] expr = unescape(comp[bracket_pos..length], '[', ']');
00949
00950 n = atoi(expr);
00951
00952 if (n)
00953 n = n - 1;
00954
00955 int at_pos = .find(expr, '@');
00956
00957 if (at_pos != -1) {
00958 expr = expr[at_pos+1 .. length];
00959 int equal_pos = .find(expr, '=');
00960
00961
00962 if (equal_pos != -1) {
00963 attr_name = unescape(expr[0 .. equal_pos]);
00964 attr_value = unescape(expr[equal_pos+1 .. length]);
00965 }
00966 }
00967 }
00968
00969 XMLNode child;
00970
00971 if (!attr_name.length)
00972
00973 child = find(child_name, n);
00974 else
00975
00976 child = find(child_name, attr_name, attr_value, n);
00977
00978 if (!child && create) {
00979 child = new XMLNode(child_name);
00980 add_child(child);
00981
00982 if (attr_name.length)
00983 this[attr_name] = attr_value;
00984 }
00985
00986 return child;
00987 }
00988 };
00989
00990
00991
00992 class XMLChildrenFilter
00993 {
00994 public:
00995
00996 this(XMLNode.Children children, char[] name)
00997 {
00998 _children = children;
00999 _filter_name = name;
01000 }
01001
01002 this(XMLNode node, char[] name)
01003 {
01004 this(node.get_children(), name);
01005 }
01006
01007 int opApply(int delegate(inout XMLNode) dg)
01008 {
01009 int result = 0;
01010
01011 foreach(child; _children)
01012 if (child._name == _filter_name) {
01013 result = dg(child);
01014
01015 if (result)
01016 break;
01017 }
01018
01019 return result;
01020 }
01021
01022 protected:
01023 XMLNode.Children _children;
01024 char[] _filter_name;
01025 };
01026
01027
01028
01029 class XMLPos
01030 {
01031 public:
01032
01033 this(XMLNode root)
01034 {
01035 _root = root;
01036 _cur = root;
01037
01038 _stack = new stack!(XMLNode);
01039 }
01040
01041 this(XMLPos other)
01042 {
01043 _root = other._root;
01044 _cur = other._cur;
01045
01046
01047 _stack = new stack!(XMLNode);
01048 }
01049
01050 this(XMLNode node, char[] name)
01051 {
01052 this(node);
01053
01054 smart_create(name);
01055 }
01056
01057 this(XMLNode node, char[] name, char[] attr_name, char[] attr_value)
01058 {
01059 this(node);
01060
01061 smart_create(name, attr_name, attr_value);
01062 }
01063
01064 this(XMLPos other, char[] name)
01065 {
01066 this(other);
01067
01068 smart_create(name);
01069 }
01070
01071 this(XMLPos other, char[] name, char[] attr_name, char[] attr_value)
01072 {
01073 this(other);
01074
01075 smart_create(name, attr_name, attr_value);
01076 }
01077
01078
01079 XMLNode cur()
01080 {
01081 return _cur;
01082 }
01083
01084
01085 XMLNode opCast() {return _cur;}
01086
01087
01088 XMLNode opCall()
01089 {
01090 return _cur;
01091 }
01092
01093
01094 char[] get(in char[] attr_name, in char[] def="")
01095 {
01096 return _cur.get(attr_name, def);
01097 }
01098
01099
01100 void put(in char[] attr_name, in char[] value)
01101 {
01102 _cur.put(attr_name, value);
01103 }
01104
01105 char[] opIndex(in char[] attr_name)
01106 {
01107 return _cur[attr_name];
01108 }
01109
01110 void opIndexAssign(in char[] attr_name, in char[] value)
01111 {
01112 _cur[attr_name] = value;
01113 }
01114
01115
01116 void add_down(XMLNode child)
01117 {
01118 _cur.add_child(child);
01119 go_to(child);
01120 }
01121
01122
01123 bool back()
01124 {
01125 if (!_stack.empty()) {
01126 _cur = _stack.pop();
01127 return true;
01128 } else
01129 return false;
01130 }
01131
01132
01133 bool go_down()
01134 {
01135 XMLNode node = _cur.get_first_child();
01136
01137 if (node) {
01138 go_to(node);
01139 return true;
01140 } else
01141 return false;
01142 }
01143
01144
01145 bool go_down(in char[] name, int n=0)
01146 {
01147 XMLNode node = _cur.find(name, n);
01148
01149 if (node) {
01150 go_to(node);
01151 return true;
01152 } else
01153 return false;
01154 }
01155
01156
01157 bool go(in char[] path)
01158 {
01159 XMLNode node = _cur;
01160
01161
01162 if (path.length>0 && path[0]=='/') {
01163 node = _root;
01164 path = path[1..length];
01165 }
01166
01167 node = node.find_relative(path);
01168
01169 if (node) {
01170 go_to(node);
01171 return true;
01172 } else
01173 return false;
01174 }
01175
01176
01177 bool create_relative(in char[] path)
01178 {
01179 XMLNode node = _cur.create_relative(path);
01180 if (!node)
01181 return false;
01182
01183 go_to(node);
01184 return true;
01185 }
01186
01187
01188 void create(in char[] name)
01189 {
01190 add_down(new XMLNode(name));
01191 }
01192
01193
01194 void smart_create(in char[] name)
01195 {
01196 XMLNode node = _cur.find(name);
01197
01198 if (node)
01199 go_to(node);
01200 else
01201 add_down(new XMLNode(name));
01202 }
01203
01204
01205 void smart_create(in char[] name, char[] attr_name, char[] attr_value)
01206 {
01207 XMLNode node = _cur.find(name, attr_name, attr_value);
01208
01209 if (node)
01210 go_to(node);
01211 else {
01212 node = new XMLNode(name);
01213 add_down(node);
01214 node.put(attr_name, attr_value);
01215 }
01216 }
01217
01218 char[] str() {return _cur._name;}
01219
01220 protected:
01221 XMLNode _root;
01222 XMLNode _cur;
01223 stack!(XMLNode) _stack;
01224
01225
01226 void go_to(XMLNode child)
01227 {
01228 _stack.push(_cur);
01229 _cur = child;
01230 }
01231 };
01232
01233
01234
01235 class XMLBool
01236 {
01237 public:
01238 this(bool value=false)
01239 {
01240 _value = value;
01241 }
01242
01243 this(in char[] value, bool def=false)
01244 {
01245 if (value && *value)
01246 _value = !icmp(value, XS_TRUE);
01247 else
01248 _value = def;
01249 }
01250
01251 this(XMLNode node, char[] attr_name, bool def=false)
01252 {
01253 char[] value = node.get(attr_name);
01254
01255 if (value.length)
01256 _value = !icmp(value, XS_TRUE);
01257 else
01258 _value = def;
01259 }
01260
01261 bool opCast()
01262 {
01263 return _value;
01264 }
01265
01266 bool opNot()
01267 {
01268 return !_value;
01269 }
01270
01271 char[] str()
01272 {
01273 return _value? XS_TRUE: XS_FALSE;
01274 }
01275
01276
01277 protected:
01278 bool _value;
01279
01280 private:
01281 void opAssign(XMLBool);
01282 };
01283
01284
01285 class XMLBoolRef
01286 {
01287 public:
01288 this(XMLNode node, char[] attr_name, bool def=false)
01289 {
01290 _pref = node.get_ref(attr_name);
01291
01292 if (!(*_pref).length)
01293 assign(def);
01294 }
01295
01296 bool opCast()
01297 {
01298 return !icmp(*_pref, XS_TRUE);
01299 }
01300
01301 bool opNot()
01302 {
01303 return icmp(*_pref, XS_TRUE)? true: false;
01304 }
01305
01306 void toggle()
01307 {
01308 assign(!opNot());
01309 }
01310
01311 XMLBoolRef opAssign(bool value)
01312 {
01313 assign(value);
01314
01315 return this;
01316 }
01317
01318 void assign(bool value)
01319 {
01320 *_pref = value? XS_TRUE: XS_FALSE;
01321 }
01322
01323 protected:
01324 char[]* _pref;
01325 };
01326
01327
01328
01329 class XMLInt
01330 {
01331 public:
01332 this(int value)
01333 {
01334 _value = value;
01335 }
01336
01337 this(in char[] value, int def=0)
01338 {
01339 if (value && *value)
01340 _value = cast(int)atoi(value);
01341 else
01342 _value = def;
01343 }
01344
01345 this(XMLNode node, char[] attr_name, int def=0)
01346 {
01347 char[] value = node.get(attr_name);
01348
01349 if (value.length)
01350 _value = cast(int)atoi(value);
01351 else
01352 _value = def;
01353 }
01354
01355 int opCast()
01356 {
01357 return _value;
01358 }
01359
01360 char[] str()
01361 {
01362 return format(XS_INTFMT, _value);
01363 }
01364
01365 protected:
01366 int _value;
01367
01368 private:
01369 void opAssign(XMLInt);
01370 };
01371
01372
01373 class XMLIntRef
01374 {
01375 public:
01376 this(XMLNode node, char[] attr_name, int def=0)
01377 {
01378 _pref = node.get_ref(attr_name);
01379
01380 if (!(*_pref).length)
01381 assign(def);
01382 }
01383
01384 XMLIntRef opAssign(int value)
01385 {
01386 assign(value);
01387
01388 return this;
01389 }
01390
01391 int opCast()
01392 {
01393 return atoi(*_pref);
01394 }
01395
01396 void assign(int value)
01397 {
01398 *_pref = format(XS_INTFMT, value);
01399 }
01400
01401 protected:
01402 char[]* _pref;
01403 };
01404
01405
01406
01407 class XMLDouble
01408 {
01409 public:
01410 this(double value)
01411 {
01412 _value = value;
01413 }
01414
01415 this(in char[] value, double def=0.)
01416 {
01417 if (value && *value)
01418 _value = toDouble(value);
01419 else
01420 _value = def;
01421 }
01422
01423 this(XMLNode node, char[] attr_name, double def=0.)
01424 {
01425 char[] value = node.get(attr_name);
01426
01427 if (value.length)
01428 _value = toDouble(value);
01429 else
01430 _value = def;
01431 }
01432
01433 double opCast()
01434 {
01435 return _value;
01436 }
01437
01438 char[] str()
01439 {
01440 return format(XS_FLOATFMT, _value);
01441 }
01442
01443 protected:
01444 double _value;
01445
01446 private:
01447 void opAssign(XMLDouble);
01448 };
01449
01450
01451 class XMLDoubleRef
01452 {
01453 public:
01454 this(XMLNode node, char[] attr_name, double def=0.)
01455 {
01456 _pref = node.get_ref(attr_name);
01457
01458 if (!(*_pref).length)
01459 assign(def);
01460 }
01461
01462 XMLDoubleRef opAssign(double value)
01463 {
01464 assign(value);
01465
01466 return this;
01467 }
01468
01469 double opCast()
01470 {
01471 return toDouble(*_pref);
01472 }
01473
01474 void assign(double value)
01475 {
01476 *_pref = format(XS_FLOATFMT, value);
01477 }
01478
01479 protected:
01480 char[]* _pref;
01481 };
01482
01483
01484
01485 class XMLString
01486 {
01487 public:
01488 this(in char[] value)
01489 {
01490 _value = value;
01491 }
01492
01493 this(in char[] value, in char[] def=XS_EMPTY)
01494 {
01495 if (value && *value)
01496 _value = value;
01497 else
01498 _value = def;
01499 }
01500
01501 this(XMLNode node, char[] attr_name, char[] def=XS_EMPTY)
01502 {
01503 char[] value = node.get(attr_name);
01504
01505 if (value.length)
01506 _value = value;
01507 else
01508 _value = def;
01509 }
01510
01511 char[] str()
01512 {
01513 return _value;
01514 }
01515
01516 protected:
01517 char[] _value;
01518
01519 private:
01520 void opAssign(XMLString);
01521 };
01522
01523
01524 class XMLStringRef
01525 {
01526 public:
01527 this(XMLNode node, char[] attr_name, char[] def=XS_EMPTY)
01528 {
01529 _pref = node.get_ref(attr_name);
01530
01531 if (!(*_pref).length)
01532 assign(def);
01533 }
01534
01535 this(in char[] node_name, XMLNode node, char[] attr_name, char[] def=XS_EMPTY)
01536 {
01537 _pref = node.subvalue_ref(node_name, attr_name);
01538
01539 if (!(*_pref).length)
01540 assign(def);
01541 }
01542
01543 XMLStringRef opAssign(in char[] value)
01544 {
01545 assign(value);
01546
01547 return this;
01548 }
01549
01550 char[] str()
01551 {
01552 return *_pref;
01553 }
01554
01555 void assign(in char[] value)
01556 {
01557 *_pref = value;
01558 }
01559
01560 protected:
01561 char[]* _pref;
01562 };
01563
01564
01565 void read_option(T)(out T var, XMLPos cfg, char[] key)
01566 {
01567 char[] val = cfg.get(key);
01568
01569 if (!val.empty())
01570 var = val;
01571 }
01572
01573 void read_option(T)(out int var, XMLPos cfg, char[] key)
01574 {
01575 char[] val = cfg.get(key);
01576
01577 if (!val.empty())
01578 var = atoi(val);
01579 }
01580
01581
01582
01583
01584 class Buffer
01585 {
01586 this()
01587 {
01588 _buffer.length = BUFFER_LEN;
01589
01590 reset();
01591 }
01592
01593 void reset()
01594 {
01595 _idx_write = 0;
01596 _buffer_str.length = 0;
01597 }
01598
01599 void append(char c)
01600 {
01601 if (_idx_write >= _buffer.length)
01602 _buffer.length = _buffer.length << 1;
01603
01604 _buffer[_idx_write++] = c;
01605 }
01606
01607 char[] str()
01608 {
01609 _buffer_str = _buffer[0 .. _idx_write];
01610
01611 return _buffer_str;
01612 }
01613
01614 size_t len()
01615 {
01616 return _idx_write;
01617 }
01618
01619 bool has_CDEnd()
01620 {
01621 return _idx_write>=3 && _buffer[0..2]=="]]>";
01622 }
01623
01624 char[] get_tag()
01625 {
01626 int i;
01627
01628
01629 if (_buffer_str.length>=1 && _buffer_str[0]=='<') {
01630 if (_buffer_str.length>=2 && _buffer_str[1]=='/')
01631 i = 2;
01632 else
01633 i = 1;
01634 } else
01635 i = 0;
01636
01637 int j = i;
01638
01639 if (j<_buffer_str.length && _buffer_str[j]=='?')
01640 ++j;
01641
01642
01643 while(j<_buffer_str.length && isxmlsym(_buffer_str[j]))
01644 ++j;
01645
01646 return _buffer_str[i..j];
01647 }
01648
01649
01650 void get_attributes(out XMLNode.AttributeMap attributes)
01651 {
01652 int i;
01653
01654
01655 if (_buffer_str.length>=1 && _buffer_str[0]=='<') {
01656 if (_buffer_str.length>=2 && (_buffer_str[1]=='/' || _buffer_str[i]=='?'))
01657 i = 2;
01658 else
01659 i = 1;
01660 } else
01661 i = 0;
01662
01663
01664 while(i<_buffer_str.length && isxmlsym(_buffer_str[i]))
01665 ++i;
01666
01667
01668 while(i<_buffer_str.length && _buffer_str[i]!='>' && _buffer_str[i]!='/') {
01669 while(isspace(cast(dchar)_buffer_str[i]))
01670 ++i;
01671
01672 int attr_idx = i;
01673
01674 while(isxmlsym(_buffer_str[i]))
01675 ++i;
01676
01677 if (i>=_buffer_str.length || _buffer_str[i] != '=')
01678 break;
01679
01680 int attr_end = i;
01681
01682 if (++i>=_buffer_str.length || _buffer_str[i]!='"' && _buffer_str[i]!='\'')
01683 break;
01684
01685 char delim = _buffer_str[i];
01686 int value_idx = ++i;
01687
01688 while(i<_buffer_str.length && _buffer_str[i]!=delim)
01689 ++i;
01690
01691 int value_end = i;
01692
01693 if (i < _buffer_str.length)
01694 ++i;
01695
01696 char[] name_str = _buffer_str[attr_idx .. attr_end];
01697 char[] value_str = _buffer_str[value_idx .. value_end];
01698
01699 attributes[name_str.dup] = DecodeXMLString(value_str).dup;
01700 }
01701 }
01702
01703 protected:
01704 char[] _buffer;
01705 int _idx_write;
01706 char[] _buffer_str;
01707 };
01708
01709
01710
01711
01712 class XMLReaderBase
01713 {
01714 public:
01715
01716 this(XMLNode node)
01717 {
01718 _pos = new XMLPos(node);
01719 _endl_defined = false;
01720 _utf8 = false;
01721 _last_tag = TAG.TAG_NONE;
01722
01723 _format = new XMLFormat;
01724 _errors = new XMLErrorList;
01725 _warnings = new XMLErrorList;
01726 }
01727
01728 bool parse()
01729 {
01730 Buffer buffer = new Buffer;
01731 int c = get();
01732 bool in_comment = false;
01733
01734 while(c != EOF) {
01735 if (in_comment || c=='<') {
01736 buffer.append(c);
01737
01738
01739 for(;;) {
01740 c = get();
01741
01742 if (c == EOF)
01743 break;
01744
01745 buffer.append(c);
01746
01747 if (c == '>')
01748 break;
01749 }
01750
01751 char[] b = buffer.str();
01752
01753 if (in_comment || (b.length>=4 && b[1..4]=="!--")) {
01754
01755 DefaultHandler(b);
01756
01757 if (b.length>=3 && b[length-3..length]=="-->")
01758 in_comment = true;
01759 else
01760 in_comment = false;
01761
01762 c = get();
01763 } else if (b.length >= 2) {
01764 if (b[1] == '/') {
01765
01766
01767
01768
01769
01770
01771
01772
01773
01774
01775 EndElementHandler();
01776
01777 c = get();
01778 } else if (b[1] == '?') {
01779
01780 char[] tag = buffer.get_tag();
01781
01782 if (tag == "?xml") {
01783 XMLNode.AttributeMap attributes;
01784 buffer.get_attributes(attributes);
01785
01786 char[]* ver = "version" in attributes;
01787 char[]* encoding = "encoding" in attributes;
01788
01789 int standalone;
01790 char[]* found = "standalone" in attributes;
01791 if (found)
01792 standalone = !icmp(*found, "yes");
01793 else
01794 standalone = -1;
01795
01796 XmlDeclHandler(ver, encoding, standalone);
01797
01798 if (encoding && !icmp(*encoding, "utf-8"))
01799 _utf8 = true;
01800
01801 c = eat_endl();
01802 } else if (tag == "?xml-stylesheet") {
01803 XMLNode.AttributeMap attributes;
01804 buffer.get_attributes(attributes);
01805
01806 auto phref = "href" in attributes;
01807 auto ptype = "type" in attributes;
01808 auto palternate = "alternate" in attributes;
01809
01810 StyleSheet stylesheet = new StyleSheet(phref?*phref:"", ptype?*ptype:"", palternate?!icmp(*palternate,"yes"):false);
01811
01812 auto ptitle = "title" in attributes;
01813 if (ptitle)
01814 stylesheet._title = *ptitle;
01815
01816 auto pmedia = "media" in attributes;
01817 if (pmedia)
01818 stylesheet._media = *pmedia;
01819
01820 auto pcharset = "charset" in attributes;
01821 if (pcharset)
01822 stylesheet._charset = *pcharset;
01823
01824 _format._stylesheets.push_back(stylesheet);
01825
01826 c = eat_endl();
01827 } else {
01828 DefaultHandler(b);
01829 c = get();
01830 }
01831 } else if (b[1] == '!') {
01832 if (b.length>=10 && b[2..10]=="DOCTYPE ") {
01833 _format._doctype.parse(b[10..length]);
01834
01835 c = eat_endl();
01836 } else if (b.length>=9 && b[2..9]=="[CDATA[") {
01837
01838 while(!buffer.has_CDEnd()) {
01839 c = get();
01840
01841 if (c == EOF)
01842 break;
01843
01844 buffer.append(c);
01845 }
01846
01847 DefaultHandler(buffer.str());
01848
01849 c = get();
01850 }
01851 } else {
01852
01853 char[] tag = buffer.get_tag();
01854
01855 if (tag.length) {
01856 XMLNode.AttributeMap attributes;
01857 buffer.get_attributes(attributes);
01858
01859 StartElementHandler(tag.dup, attributes);
01860
01861 if (b[length-2] == '/')
01862 EndElementHandler();
01863 }
01864
01865 c = get();
01866 }
01867 } else
01868 c = get();
01869 } else {
01870 buffer.append(c);
01871
01872
01873 for(;;) {
01874
01875 if (!_endl_defined)
01876 if (c == '\n') {
01877 _format._endl = "\n";
01878 _endl_defined = true;
01879 } else if (c == '\r') {
01880 _format._endl = "\r\n";
01881 _endl_defined = true;
01882 }
01883
01884 c = get();
01885
01886 if (c == EOF)
01887 break;
01888
01889 if (c == '<')
01890 break;
01891
01892 buffer.append(c);
01893 }
01894
01895 DefaultHandler(buffer.str());
01896 }
01897
01898 buffer.reset();
01899 }
01900
01901 return true;
01902 }
01903
01904 public:
01905
01906
01907 void read()
01908 {
01909 if (!parse()) {
01910 XMLError error;
01911
01912 error._message = "XML parsing error";
01913
01914
01915
01916 _errors.push_back(error);
01917 }
01918
01919 finish_read();
01920 }
01921
01922
01923 char[] get_position()
01924 {
01925
01926
01927
01928 return "";
01929 }
01930
01931
01932 XMLFormat get_format() {return _format;}
01933 char[] get_endl() {return _endl_defined? _format._endl: "\n";}
01934
01935 XMLErrorList get_errors() {return _errors;}
01936 XMLErrorList get_warnings() {return _warnings;}
01937
01938 void clear_errors()
01939 {
01940 _errors.clear();
01941 _warnings.clear();
01942 }
01943
01944 version(XMLNODE_LOCATION) {
01945
01946 char[] _display_path;
01947
01948 XMLLocation get_location()
01949 {
01950 return new XMLLocation;
01951 }
01952
01953 }
01954
01955 protected:
01956 XMLPos _pos;
01957
01958 char[] _content;
01959
01960 enum TAG {TAG_NONE, TAG_START, TAG_END};
01961 TAG _last_tag;
01962
01963 XMLErrorList _errors;
01964 XMLErrorList _warnings;
01965
01966 XMLFormat _format;
01967 bool _endl_defined;
01968
01969 abstract int get();
01970
01971 int eat_endl()
01972 {
01973 int c = get();
01974
01975 if (c == '\r')
01976 c = get();
01977
01978 if (c == '\n')
01979 c = get();
01980
01981 return c;
01982 }
01983
01984 bool _utf8;
01985
01986 void finish_read()
01987 {
01988 if (_pos()._children.empty())
01989 _pos()._trailing ~= _content;
01990 else
01991 _pos()._children.back()._trailing ~= _content;
01992
01993 _content.length = 0;
01994 }
01995
01996
01997 void XmlDeclHandler(in char[]* ver, char[]* encoding, int standalone)
01998 {
01999 if (ver)
02000 _format._version = *ver;
02001
02002 if (encoding)
02003 _format._encoding = *encoding;
02004
02005 _format._standalone = standalone;
02006 }
02007
02008
02009 void StartElementHandler(in char[] name, XMLNode.AttributeMap attributes)
02010 {
02011 int i = 0;
02012
02013
02014 for(i=_content.length; i>0; --i)
02015 if (!isspace(cast(dchar)_content[i-1]))
02016 break;
02017
02018 if (i > 0)
02019 if (_pos()._children.empty()) {
02020 if (_last_tag == TAG.TAG_START)
02021 _pos()._content ~= _content[0 .. i];
02022 else if (_last_tag == TAG.TAG_END)
02023 _pos()._trailing ~= _content[0 .. i];
02024 else
02025 i = 0;
02026 } else
02027 _pos()._children.back()._trailing ~= _content[0 .. i];
02028
02029 char[] leading;
02030
02031 if (i < _content.length)
02032 leading = _content[i .. length];
02033
02034 XMLNode node = new XMLNode(name, leading);
02035
02036 _pos.add_down(node);
02037
02038 version(XMLNODE_LOCATION) {
02039 node._location = get_location();
02040 }
02041
02042 node._attributes = attributes;
02043
02044 _last_tag = TAG.TAG_START;
02045 _content.length = 0;
02046 }
02047
02048
02049 void EndElementHandler()
02050 {
02051 int s = 0;
02052 int i = 0;
02053 int e = _content.length;
02054
02055 if (_content.length>=9+3 &&
02056 _content[0..9]=="<![CDATA[" && _content[length-3..length]=="]]>") {
02057 s += 9;
02058 i = (e-=3);
02059 } else {
02060
02061 for(i=e; i>0; --i)
02062 if (!isspace(cast(dchar)_content[i-1]))
02063 break;
02064 }
02065
02066 if (i > s)
02067 if (_pos()._children.empty())
02068 _pos()._content ~= _content[s .. i];
02069 else if (_last_tag == TAG.TAG_START)
02070 _pos()._content ~= _content[s .. i];
02071 else
02072 _pos()._children.back()._trailing ~= _content[s .. i];
02073
02074 if (i != e)
02075 _pos()._end_leading = _content[i .. e];
02076
02077 _pos.back();
02078
02079 _last_tag = TAG.TAG_END;
02080 _content.length = 0;
02081 }
02082
02083
02084 void DefaultHandler(in char[] s)
02085 {
02086 _content ~= s;
02087 }
02088 };
02089
02090
02091
02092 class XMLReader : public XMLReaderBase
02093 {
02094 public:
02095 this(XMLNode node, Stream i)
02096 {
02097 super(node);
02098 _in = i;
02099 }
02100
02101
02102 int get()
02103 {
02104 int c = _in.getc();
02105
02106 return !_in.eof()? c: EOF;
02107 }
02108
02109 protected:
02110 Stream _in;
02111 };
02112
02113
02114
02115 class XMLDoc : public XMLNode
02116 {
02117 public:
02118 this()
02119 {
02120 super("");
02121
02122 _format = new XMLFormat;
02123 _errors = new XMLErrorList;
02124 }
02125
02126 this(in char[] path)
02127 {
02128 this();
02129
02130 read_file(path);
02131 }
02132
02133 bool read_file(in char[] path)
02134 {
02135 Stream i = new BufferedFile(path, FileMode.In);
02136 scope(exit) i.close();
02137
02138 XMLReader reader = new XMLReader(this, i);
02139
02140 return read(reader, path);
02141 }
02142
02143 bool read_buffer(in char[] buffer, in char[] system_id="")
02144 {
02145 return read(new MemoryStream(buffer), system_id);
02146 }
02147
02148 bool read(in Stream i, in char[] system_id="")
02149 {
02150 XMLReader reader = new XMLReader(this, i);
02151
02152 return read(reader, system_id);
02153 }
02154
02155 bool read(XMLReaderBase reader, char[] display_path)
02156 {
02157 version(XMLNODE_LOCATION) {
02158
02159 _display_path = display_path.dup;
02160 reader._display_path = _display_path;
02161 }
02162
02163 reader.clear_errors();
02164 reader.read();
02165
02166 _format = reader.get_format();
02167 _format._endl = reader.get_endl();
02168
02169 if (!reader.get_errors().empty()) {
02170 _errors = reader.get_errors();
02171 return false;
02172 }
02173
02174 return true;
02175 }
02176
02177
02178 void write(Stream o, WRITE_MODE mode=WRITE_MODE.FORMAT_SMART)
02179 {
02180 _format.print_header(o, mode!=WRITE_MODE.FORMAT_PLAIN);
02181
02182 if (_children.length() == 1)
02183 _children.front().write(o, _format, mode);
02184 else if (!_children.empty()) {
02185
02186 return false;
02187 }
02188 }
02189
02190
02191 void write_formating(Stream o)
02192 {
02193 write(o, WRITE_MODE.FORMAT_PRETTY);
02194 }
02195
02196 void write_file(in char[] path, WRITE_MODE mode=WRITE_MODE.FORMAT_SMART)
02197 {
02198 Stream o = new BufferedFile(path, FileMode.Out);
02199 scope(exit) o.close();
02200
02201 write(o, mode);
02202 }
02203
02204 void write_formating(in char[] path)
02205 {
02206 Stream o = new BufferedFile(path, FileMode.Out);
02207 scope(exit) o.close();
02208
02209 write_formating(o);
02210 }
02211
02212 XMLFormat _format;
02213 XMLErrorList _errors;
02214
02215 version(XMLNODE_LOCATION) {
02216 char[] _display_path;
02217 }
02218 };
02219
02220
02221
02222 class XMLMessage : public XMLDoc
02223 {
02224 public:
02225 this(in char[] name)
02226 {
02227 _pos = new XMLPos(this);
02228 _pos.create(name);
02229 }
02230
02231 char[] toString()
02232 {
02233 Stream m = new MemoryStream();
02234
02235 write(m);
02236
02237 return m.toString();
02238 }
02239
02240 XMLPos _pos;
02241
02242 protected:
02243 this()
02244 {
02245 _pos = new XMLPos(this);
02246 }
02247 };
02248
02249
02250
02251 class XMLMessageFromString : public XMLMessage
02252 {
02253 public:
02254 this(in char[] xml_str, in char[] system_id="")
02255 {
02256 read_buffer(xml_str, system_id);
02257 }
02258 };
02259
02260
02261
02262 class XMLMessageReader : public XMLPos
02263 {
02264 public:
02265 this(in char[] xml_str, in char[] system_id="")
02266 {
02267 super(_msg);
02268
02269 _msg.read_buffer(xml_str, system_id);
02270 }
02271
02272 XMLDoc get_document()
02273 {
02274 return _msg;
02275 }
02276
02277 protected:
02278 XMLDoc _msg;
02279 };
02280
02281
02282
02283 class XMLWriter
02284 {
02285 private:
02286 this(XMLFormat format, Stream o)
02287 {
02288 _out = o;
02289 _format = format;
02290 _stack = new stack!(StackEntry);
02291
02292 format.print_header(_out, false);
02293 }
02294
02295 public:
02296 this(Stream o, in XMLFormat format=new XMLFormat)
02297 {
02298 _own_stream = null;
02299
02300 this(format, o);
02301 }
02302
02303 this(in char[] path, in XMLFormat format=new XMLFormat)
02304 {
02305 _format = format;
02306
02307 _own_stream = new BufferedFile(new File(path, FileMode.Out));
02308
02309 this(format, _own_stream);
02310 }
02311
02312 ~this()
02313 {
02314 close();
02315 }
02316
02317 void close()
02318 {
02319 if (_out) {
02320 _out.writeString(_format._endl);
02321
02322 if (_own_stream) {
02323 _own_stream.close();
02324 delete _own_stream;
02325 }
02326
02327 _out = null;
02328 }
02329 }
02330
02331
02332 void create(in char[] name)
02333 {
02334 if (!_stack.empty()) {
02335 StackEntry last = _stack.top();
02336
02337 if (last._state < WRITESTATE.PRE_CLOSED) {
02338 write_attributes(last);
02339 close_pre(last);
02340 }
02341
02342 last._children = true;
02343 }
02344
02345 auto entry = new StackEntry;
02346 entry._node_name = name;
02347 _stack.push(entry);
02348
02349 write_pre(entry);
02350 }
02351
02352
02353 bool back()
02354 {
02355 if (!_stack.empty()) {
02356 write_post(_stack.top());
02357
02358 _stack.pop();
02359 return true;
02360 } else
02361 return false;
02362 }
02363
02364
02365 void put(in char[] attr_name, in char[] value)
02366 {
02367 if (!_stack.empty())
02368 _stack.top()._attributes[attr_name] = value;
02369 }
02370
02371
02372 void opIndexAssign(in char[] attr_name, in char[] value)
02373 {
02374 if (!_stack.empty())
02375 _stack.top()._attributes[attr_name] = value;
02376 }
02377
02378 void set_content(in char[] s, bool cdata=false)
02379 {
02380 if (!_stack.empty())
02381 _stack.top()._content = EncodeXMLString(s, cdata);
02382 }
02383
02384
02385 enum WRITESTATE {
02386 NOTHING, ATTRIBUTES, PRE_CLOSED, POST
02387 };
02388
02389 protected:
02390 Stream _own_stream;
02391 Stream _out;
02392 XMLFormat _format;
02393
02394 typedef XMLNode.AttributeMap AttrMap;
02395
02396
02397 class StackEntry {
02398 public:
02399 char[] _node_name;
02400 AttrMap _attributes;
02401 char[] _content;
02402 WRITESTATE _state;
02403 bool _children;
02404
02405 this()
02406 {
02407 _state = WRITESTATE.NOTHING;
02408 _children = false;
02409 }
02410 };
02411
02412 stack!(StackEntry) _stack;
02413
02414 void close_pre(StackEntry entry)
02415 {
02416 _out.writeString(">");
02417
02418 entry._state = WRITESTATE.PRE_CLOSED;
02419 }
02420
02421 void write_pre(StackEntry entry)
02422 {
02423 if (_format._pretty >= PRETTY_FLAGS.PRETTY_LINEFEED)
02424 _out.writeString(_format._endl);
02425
02426 if (_format._pretty == PRETTY_FLAGS.PRETTY_INDENT)
02427 for(size_t i=_stack.length(); --i>0; )
02428 _out.writeString(XML_INDENT_SPACE);
02429
02430 _out.writeString('<' ~ EncodeXMLString(entry._node_name));
02431
02432 }
02433
02434 void write_attributes(StackEntry entry)
02435 {
02436 foreach(key,value; entry._attributes)
02437 _out.writeString(' ' ~ EncodeXMLString(key) ~ "=\"" ~ EncodeXMLString(value) ~ "\"");
02438
02439 entry._state = WRITESTATE.ATTRIBUTES;
02440 }
02441
02442 void write_post(StackEntry entry)
02443 {
02444 if (entry._state < WRITESTATE.ATTRIBUTES)
02445 write_attributes(entry);
02446
02447 if (entry._children || entry._content.length) {
02448 if (entry._state < WRITESTATE.PRE_CLOSED)
02449 close_pre(entry);
02450
02451 _out.writeString(entry._content);
02452
02453
02454 if (_format._pretty>=PRETTY_FLAGS.PRETTY_LINEFEED && !entry._content.length)
02455 _out.writeString(_format._endl);
02456
02457 if (_format._pretty==PRETTY_FLAGS.PRETTY_INDENT && !entry._content.length)
02458 for(size_t i=_stack.length(); --i>0; )
02459 _out.writeString(XML_INDENT_SPACE);
02460
02461 _out.writeString("</" ~ EncodeXMLString(entry._node_name) ~ ">");
02462 } else {
02463 _out.writeString("/>");
02464 }
02465
02466 entry._state = WRITESTATE.POST;
02467 }
02468 };
02469
02470
02471
02472 char[] unescape(in char[] str, char b, char e)
02473 {
02474 if (str.length>0 && str[0]==b && str[length-1]==e)
02475 return str[1 .. length-1];
02476 else
02477 return str;
02478 }
02479
02480 char[] unescape(in char[] s)
02481 {
02482 return unescape(s, '"', '"');
02483 }
02484
02485
02486
02487 char[] EncodeXMLString(in char[] str, bool cdata=false)
02488 {
02489 char[] s = str;
02490 size_t l = str.length;
02491
02492 if (cdata) {
02493
02494 char[] ret = "<![CDATA[";
02495
02496 ret ~= str;
02497
02498 ret ~= "]]>";
02499
02500 return ret;
02501 } else if (l <= BUFFER_LEN) {
02502 char[] buffer;
02503 buffer.length = 6*str.length;
02504 int w = 0;
02505
02506 foreach(char c; s)
02507 switch(c) {
02508 case '&':
02509 buffer[w++] = '&'; buffer[w++] = 'a'; buffer[w++] = 'm'; buffer[w++] = 'p'; buffer[w++] = ';';
02510 break;
02511
02512 case '<':
02513 buffer[w++] = '&'; buffer[w++] = 'l'; buffer[w++] = 't'; buffer[w++] = ';';
02514 break;
02515
02516 case '>':
02517 buffer[w++] = '&'; buffer[w++] = 'g'; buffer[w++] = 't'; buffer[w++] = ';';
02518 break;
02519
02520 case '"':
02521 buffer[w++] = '&'; buffer[w++] = 'q'; buffer[w++] = 'u'; buffer[w++] = 'o'; buffer[w++] = 't'; buffer[w++] = ';';
02522 break;
02523
02524 case '\'':
02525 buffer[w++] = '&'; buffer[w++] = 'a'; buffer[w++] = 'p'; buffer[w++] = 'o'; buffer[w++] = 's'; buffer[w++] = ';';
02526 break;
02527
02528 default:
02529 if (cast(dchar)c<20 && c!='\t' && c!='\r' && c!='\n') {
02530 char[] b = format("&%d;", cast(int)c);
02531 foreach(cc; b)
02532 buffer[w++] = cc;
02533 } else
02534 buffer[w++] = c;
02535 }
02536
02537 buffer.length = w;
02538
02539 return buffer;
02540 }
02541 else {
02542
02543 Stream o = new MemoryStream;
02544
02545 foreach(c; str)
02546 switch(c) {
02547 case '&':
02548 o.writeString("&");
02549 break;
02550
02551 case '<':
02552 o.writeString("<");
02553 break;
02554
02555 case '>':
02556 o.writeString(">");
02557 break;
02558
02559 case '"':
02560 o.writeString(""");
02561 break;
02562
02563 case '\'':
02564 o.writeString("'");
02565 break;
02566
02567 default:
02568 if (cast(dchar)c<20 && c!='\t' && c!='\r' && c!='\n')
02569 o.writeString(format("&%d;", cast(int)c));
02570 else
02571 o.write(c);
02572 }
02573
02574 return o.toString();
02575 }
02576 }
02577
02578 bool icmpLiteral(in char[] str, in char[] lit_str, inout int i)
02579 {
02580 if (str.length>=lit_str.length && !icmp(str[0..lit_str.length], lit_str)) {
02581 i += lit_str.length;
02582 return true;
02583 } else
02584 return false;
02585 }
02586
02587
02588 char[] DecodeXMLString(in char[] str)
02589 {
02590 char[] buffer;
02591 buffer.length = str.length;
02592
02593 size_t o = 0;
02594 bool modified = false;
02595
02596 for(int i=0; i<str.length; ++i) {
02597 if (str[i] == '&') {
02598 char[] cur = str[i+1..length];
02599
02600 if (icmpLiteral(cur, "lt;", i))
02601 {buffer[o++] = '<'; modified = true;}
02602 else if (icmpLiteral(cur, "gt;", i))
02603 {buffer[o++] = '>'; modified = true;}
02604 else if (icmpLiteral(cur, "amp;", i))
02605 {buffer[o++] = '&'; modified = true;}
02606 else if (icmpLiteral(cur, "quot;", i))
02607 {buffer[o++] = '\"'; modified = true;}
02608 else if (icmpLiteral(cur, "apos;", i))
02609 {buffer[o++] = '\''; modified = true;}
02610 else
02611 buffer[o++] = str[i];
02612 } else if (i+9<=str.length && !icmp(str[i..i+9], "<![CDATA[")) {
02613
02614 for(int e=i+9; ; ++e) {
02615 if (e+3 > str.length) {
02616 buffer[o++] = str[i];
02617 break;
02618 }
02619
02620 if (str[e..e+3] == "]]>") {
02621 i += 9;
02622 size_t l = e - i;
02623 buffer[o..o+l] = str[i..i+l];
02624 o += l;
02625 i += l + 2;
02626 modified = true;
02627 break;
02628 }
02629 }
02630 } else
02631 buffer[o++] = str[i];
02632 }
02633
02634 return modified? buffer[0..o]: str;
02635 }
02636
02637
02638 unittest
02639 {
02640 debug(string) printf("xmlstorage unittest\n");
02641
02642
02643
02644
02645
02646
02647
02648
02649 void print_content(XMLDoc doc, Stream o=dout)
02650 {
02651 doc.write(o, WRITE_MODE.FORMAT_PLAIN);
02652 }
02653
02654 void assert_content(XMLDoc doc, char[] str)
02655 {
02656 Stream m = new MemoryStream();
02657 doc.write(m, WRITE_MODE.FORMAT_PLAIN);
02658
02659
02660
02661 assert(m.toString()==str);
02662 }
02663
02664 void testEncodeDecode(in char[] a, in char[] b)
02665 {
02666 assert(DecodeXMLString(b)==a);
02667 assert(EncodeXMLString(a)==b);
02668 }
02669
02670 void testEncodeDecodeCDATA(in char[] a, in char[] b)
02671 {
02672 assert(DecodeXMLString(b)==a);
02673 assert(EncodeXMLString(a, true)==b);
02674 }
02675
02676
02677
02678 testEncodeDecode("abc", "abc");
02679 testEncodeDecode("x<>y", "x<>y");
02680 testEncodeDecode("\"&'", ""&'");
02681 testEncodeDecodeCDATA("ABC", "<![CDATA[ABC]]>");
02682
02683
02684
02685
02686 auto doc = new XMLDoc();
02687 auto pos = new XMLPos(doc);
02688
02689 pos.create("node1");
02690 pos.put("id", "1");
02691 pos["attr"] = "a";
02692
02693 pos.create_relative("a/b/c/d");
02694 pos.back();
02695
02696 pos.smart_create("node2");
02697 (new XMLIntRef(pos(), "id")) = 2;
02698 assert(pos.back());
02699
02700 pos.create("node2");
02701 pos().set_content("hello");
02702 assert(pos.back());
02703
02704 *pos().subvalue_ref("node2", "attr2", 1) = "xyz";
02705 assert(pos.back());
02706
02707 (new XMLStringRef("node1", pos(), "attr1")).assign("abc");
02708
02709 print_content(doc);
02710
02711 assert_content(doc,
02712 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
02713 "<node1 attr=\"a\" attr1=\"abc\" id=\"1\">"
02714 "<a>"
02715 "<b>"
02716 "<c>"
02717 "<d/>"
02718 "</c>"
02719 "</b>"
02720 "</a>"
02721 "<node2 id=\"2\"/>"
02722 "<node2 attr2=\"xyz\">hello</node2>"
02723 "</node1>"
02724 );
02725
02726
02727
02728
02729 pos = new XMLPos(doc);
02730
02731 bool ok = pos.go_down("node1");
02732 assert(ok); {
02733 assert(pos.get("id")=="1");
02734
02735 ok = pos.go("node2");
02736 assert(ok); {
02737 assert(pos.get("id")=="2");
02738 assert(pos.back());
02739 }
02740
02741 ok = pos.go_down("node2", 1);
02742 assert(ok); {
02743 assert(pos["attr2"]=="xyz");
02744 assert(pos.back());
02745 }
02746
02747 assert(pos.back());
02748 }
02749 assert(!pos.back());
02750
02751
02752
02753
02754 foreach(XMLNode e; new XMLChildrenFilter(doc, "node1")) {
02755 writefln(e._name);
02756
02757 foreach(XMLNode e; new XMLChildrenFilter(e, "node2")) {
02758 writefln(e._name);
02759 }
02760 }
02761
02762
02763
02764
02765 {
02766 auto w = new XMLWriter("test1.xml");
02767 scope(exit) w.close();
02768 w.create("root");
02769 w.create("level 1");
02770 w.create("level 2");
02771 w.back();
02772 w.back();
02773 w.back();
02774 }
02775
02776 auto d = new XMLDoc();
02777 assert(d.read_file("test1.xml"));
02778
02779 d.write_file("test2.xml");
02780 }