1 /* 2 * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 */ 18 19 #pragma once 20 21 #include "Exception.h" 22 #include "Streams.h" 23 #include "SimpleXMLReader.h" 24 25 namespace dcpp { 26 27 STANDARD_EXCEPTION(SimpleXMLException); 28 29 /** 30 * A simple XML class that loads an XML-ish structure into an internal tree 31 * and allows easy access to each element through a "current location". 32 */ 33 class SimpleXML : private boost::noncopyable 34 { 35 public: SimpleXML()36 SimpleXML() : root("BOGUSROOT", Util::emptyString, NULL), current(&root), found(false) { 37 resetCurrentChild(); 38 } ~SimpleXML()39 ~SimpleXML() { } 40 41 void addTag(const string& aName, const string& aData = Util::emptyString); addTag(const string & aName,int aData)42 void addTag(const string& aName, int aData) { 43 addTag(aName, Util::toString(aData)); 44 } addTag(const string & aName,int64_t aData)45 void addTag(const string& aName, int64_t aData) { 46 addTag(aName, Util::toString(aData)); 47 } 48 49 template<typename T> addAttrib(const string & aName,const T & aData)50 void addAttrib(const string& aName, const T& aData) { 51 addAttrib(aName, Util::toString(aData)); 52 } 53 54 void addAttrib(const string& aName, const string& aData); addAttrib(const string & aName,bool aData)55 void addAttrib(const string& aName, bool aData) { 56 addAttrib(aName, string(aData ? "1" : "0")); 57 } 58 59 template <typename T> addChildAttrib(const string & aName,const T & aData)60 void addChildAttrib(const string& aName, const T& aData) { 61 addChildAttrib(aName, Util::toString(aData)); 62 } 63 void addChildAttrib(const string& aName, const string& aData); addChildAttrib(const string & aName,bool aData)64 void addChildAttrib(const string& aName, bool aData) { 65 addChildAttrib(aName, string(aData ? "1" : "0")); 66 } 67 getData()68 const string& getData() const { 69 dcassert(current != NULL); 70 return current->data; 71 } 72 stepIn()73 void stepIn() { 74 checkChildSelected(); 75 current = *currentChild; 76 currentChild = current->children.begin(); 77 found = false; 78 } 79 stepOut()80 void stepOut() { 81 if(current == &root) 82 throw SimpleXMLException("Already at lowest level"); 83 84 dcassert(current->parent != NULL); 85 86 currentChild = find(current->parent->children.begin(), current->parent->children.end(), current); 87 88 current = current->parent; 89 found = true; 90 } 91 resetCurrentChild()92 void resetCurrentChild() noexcept { 93 found = false; 94 dcassert(current != NULL); 95 currentChild = current->children.begin(); 96 } 97 98 bool findChild(const string& aName) noexcept; 99 getChildData()100 const string& getChildData() const { 101 checkChildSelected(); 102 return (*currentChild)->data; 103 } 104 105 const string& getChildAttrib(const string& aName, const string& aDefault = Util::emptyString) { 106 checkChildSelected(); 107 return (*currentChild)->getAttrib(aName, aDefault); 108 } 109 getIntChildAttrib(const string & aName)110 int getIntChildAttrib(const string& aName) { 111 checkChildSelected(); 112 return Util::toInt(getChildAttrib(aName)); 113 } getLongLongChildAttrib(const string & aName)114 int64_t getLongLongChildAttrib(const string& aName) { 115 checkChildSelected(); 116 return Util::toInt64(getChildAttrib(aName)); 117 } getBoolChildAttrib(const string & aName)118 bool getBoolChildAttrib(const string& aName) { 119 checkChildSelected(); 120 const string& tmp = getChildAttrib(aName); 121 122 return !tmp.empty() && tmp[0] == '1'; 123 } 124 125 void fromXML(const string& aXML); toXML()126 string toXML() { string tmp; StringOutputStream os(tmp); toXML(&os); return tmp; } toXML(OutputStream * f)127 void toXML(OutputStream* f) { if(!root.children.empty()) root.children[0]->toXML(0, f); } 128 129 static const string& escape(const string& str, string& tmp, bool aAttrib, bool aLoading = false, const string &encoding = Text::utf8) { 130 if(needsEscape(str, aAttrib, aLoading, encoding)) { 131 tmp = str; 132 return escape(tmp, aAttrib, aLoading, encoding); 133 } 134 return str; 135 } 136 static string& escape(string& aString, bool aAttrib, bool aLoading = false, const string &encoding = Text::utf8); 137 /** 138 * This is a heuristic for whether escape needs to be called or not. The results are 139 * only guaranteed for false, i e sometimes true might be returned even though escape 140 * was not needed... 141 */ 142 static bool needsEscape(const string& aString, bool aAttrib, bool aLoading = false, const string &encoding = Text::utf8) { 143 return Util::stricmp(encoding, Text::utf8) != 0 || (((aLoading) ? aString.find('&') : aString.find_first_of(aAttrib ? "<&>'\"" : "<&>")) != string::npos); 144 } 145 static const string utf8Header; 146 private: 147 class Tag { 148 public: 149 typedef Tag* Ptr; 150 typedef vector<Ptr> List; 151 typedef List::iterator Iter; 152 153 /** 154 * A simple list of children. To find a tag, one must search the entire list. 155 */ 156 List children; 157 /** 158 * Attributes of this tag. According to the XML standard the names 159 * must be unique (case-sensitive). (Assuming that we have few attributes here, 160 * we use a vector instead of a (hash)map to save a few bytes of memory and unnecessary 161 * calls to the memory allocator...) 162 */ 163 StringPairList attribs; 164 165 /** Tag name */ 166 string name; 167 168 /** Tag data, may be empty. */ 169 string data; 170 171 /** Parent tag, for easy traversal */ 172 Ptr parent; 173 Tag(const string & aName,const StringPairList & a,Ptr aParent)174 Tag(const string& aName, const StringPairList& a, Ptr aParent) : attribs(a), name(aName), data(), parent(aParent) { 175 } 176 Tag(const string & aName,const string & d,Ptr aParent)177 Tag(const string& aName, const string& d, Ptr aParent) : name(aName), data(d), parent(aParent) { 178 } 179 180 const string& getAttrib(const string& aName, const string& aDefault = Util::emptyString) { 181 StringPairIter i = find_if(attribs.begin(), attribs.end(), CompareFirst<string,string>(aName)); 182 return (i == attribs.end()) ? aDefault : i->second; 183 } 184 void toXML(int indent, OutputStream* f); 185 186 void appendAttribString(string& tmp); 187 /** Delete all children! */ ~Tag()188 ~Tag() { 189 for(Iter i = children.begin(); i != children.end(); ++i) { 190 delete *i; 191 } 192 } 193 194 private: 195 Tag(const Tag&); 196 Tag& operator=(Tag&); 197 }; 198 199 class TagReader : public SimpleXMLReader::CallBack { 200 public: TagReader(Tag * root)201 TagReader(Tag* root) : cur(root) { } getData(string &)202 virtual bool getData(string&) { return false; } startTag(const string & name,StringPairList & attribs,bool simple)203 virtual void startTag(const string& name, StringPairList& attribs, bool simple) { 204 cur->children.push_back(new Tag(name, attribs, cur)); 205 if(!simple) 206 cur = cur->children.back(); 207 } endTag(const string &,const string & d)208 virtual void endTag(const string&, const string& d) { 209 cur->data = d; 210 if(cur->parent == NULL) 211 throw SimpleXMLException("Invalid end tag"); 212 cur = cur->parent; 213 } 214 215 Tag* cur; 216 }; 217 218 /** Bogus root tag, should have only one child! */ 219 Tag root; 220 221 /** Current position */ 222 Tag::Ptr current; 223 224 Tag::Iter currentChild; 225 checkChildSelected()226 void checkChildSelected() const noexcept { 227 dcassert(current != NULL); 228 dcassert(currentChild != current->children.end()); 229 } 230 231 bool found; 232 }; 233 234 } // namespace dcpp 235