123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- #include "xml-document.h"
- #include <libxml/xinclude.h>
- #include <libxml/xpathInternals.h>
- #include <library/cpp/xml/init/init.h>
- #include <util/generic/yexception.h>
- #include <util/folder/dirut.h>
- namespace {
- struct TInit {
- inline TInit() {
- NXml::InitEngine();
- }
- } initer;
- }
- namespace NXml {
- TDocument::TDocument(const TString& xml, Source type) {
- switch (type) {
- case File:
- ParseFile(xml);
- break;
- case String:
- ParseString(xml);
- break;
- case RootName: {
- TDocHolder doc(xmlNewDoc(XMLCHAR("1.0")));
- if (!doc)
- THROW(XmlException, "Can't create xml document.");
- doc->encoding = xmlStrdup(XMLCHAR("utf-8"));
- TNodePtr node(xmlNewNode(nullptr, XMLCHAR(xml.c_str())));
- if (!node)
- THROW(XmlException, "Can't create root node.");
- xmlDocSetRootElement(doc.Get(), node.Get());
- Y_UNUSED(node.Release());
- Doc = std::move(doc);
- } break;
- default:
- THROW(InvalidArgument, "Wrong source type");
- }
- }
- TDocument::TDocument(TDocument&& doc)
- : Doc(std::move(doc.Doc))
- {
- }
- TDocument& TDocument::operator=(TDocument&& doc) {
- if (this != &doc)
- doc.Swap(*this);
- return *this;
- }
- void TDocument::ParseFile(const TString& file) {
- if (!NFs::Exists(file))
- THROW(XmlException, "File " << file << " doesn't exist");
- TParserCtxtPtr pctx(xmlNewParserCtxt());
- if (!pctx)
- THROW(XmlException, "Can't create parser context");
- TDocHolder doc(xmlCtxtReadFile(pctx.Get(), file.c_str(), nullptr, XML_PARSE_NOCDATA));
- if (!doc)
- THROW(XmlException, "Can't parse file " << file);
- int res = xmlXIncludeProcessFlags(doc.Get(), XML_PARSE_XINCLUDE | XML_PARSE_NOCDATA | XML_PARSE_NOXINCNODE);
- if (res == -1)
- THROW(XmlException, "XIncludes processing failed");
- Doc = std::move(doc);
- }
- void TDocument::ParseString(TZtStringBuf xml) {
- TParserCtxtPtr pctx(xmlNewParserCtxt());
- if (pctx.Get() == nullptr)
- THROW(XmlException, "Can't create parser context");
- TDocHolder doc(xmlCtxtReadMemory(pctx.Get(), xml.c_str(), (int)xml.size(), nullptr, nullptr, XML_PARSE_NOCDATA));
- if (!doc)
- THROW(XmlException, "Can't parse string");
- Doc = std::move(doc);
- }
- TNode TDocument::Root() {
- xmlNode* r = xmlDocGetRootElement(Doc.Get());
- if (r == nullptr)
- THROW(XmlException, "TDocument hasn't root element");
- return TNode(Doc.Get(), r);
- }
- TConstNode TDocument::Root() const {
- xmlNode* r = xmlDocGetRootElement(Doc.Get());
- if (r == nullptr)
- THROW(XmlException, "TDocument hasn't root element");
- return TConstNode(TNode(Doc.Get(), r));
- }
- bool TNode::IsNull() const {
- return NodePointer == nullptr;
- }
- bool TNode::IsElementNode() const {
- return !IsNull() && (NodePointer->type == XML_ELEMENT_NODE);
- }
- TXPathContextPtr TNode::CreateXPathContext(const TNamespacesForXPath& nss) const {
- TXPathContextPtr ctx = xmlXPathNewContext(DocPointer);
- if (!ctx)
- THROW(XmlException, "Can't create empty xpath context");
- for (const auto& ns : nss) {
- const int r = xmlXPathRegisterNs(ctx.Get(), XMLCHAR(ns.Prefix.c_str()), XMLCHAR(ns.Url.c_str()));
- if (r != 0)
- THROW(XmlException, "Can't register namespace " << ns.Url << " with prefix " << ns.Prefix);
- }
- return ctx;
- }
- TConstNodes TNode::XPath(TZtStringBuf xpath, bool quiet, const TNamespacesForXPath& ns) const {
- TXPathContextPtr ctxt = CreateXPathContext(ns);
- return XPath(xpath, quiet, *ctxt);
- }
- TConstNodes TNode::XPath(TZtStringBuf xpath, bool quiet, TXPathContext& ctxt) const {
- if (xmlXPathSetContextNode(NodePointer, &ctxt) != 0)
- THROW(XmlException, "Can't set xpath context node, probably the context is associated with another document");
- TXPathObjectPtr obj = xmlXPathEvalExpression(XMLCHAR(xpath.c_str()), &ctxt);
- if (!obj)
- THROW(XmlException, "Can't evaluate xpath expression " << xpath);
- TConstNodes nodes(DocPointer, obj);
- if (nodes.Size() == 0 && !quiet)
- THROW(NodeNotFound, xpath);
- return nodes;
- }
- TConstNodes TNode::Nodes(TZtStringBuf xpath, bool quiet, const TNamespacesForXPath& ns) const {
- TXPathContextPtr ctxt = CreateXPathContext(ns);
- return Nodes(xpath, quiet, *ctxt);
- }
- TConstNodes TNode::Nodes(TZtStringBuf xpath, bool quiet, TXPathContext& ctxt) const {
- TConstNodes nodes = XPath(xpath, quiet, ctxt);
- if (nodes.Size() != 0 && !nodes[0].IsElementNode())
- THROW(XmlException, "xpath points to non-element nodes: " << xpath);
- return nodes;
- }
- TNode TNode::Node(TZtStringBuf xpath, bool quiet, const TNamespacesForXPath& ns) {
- TXPathContextPtr ctxt = CreateXPathContext(ns);
- return Node(xpath, quiet, *ctxt);
- }
- TConstNode TNode::Node(TZtStringBuf xpath, bool quiet, const TNamespacesForXPath& ns) const {
- TXPathContextPtr ctxt = CreateXPathContext(ns);
- return Node(xpath, quiet, *ctxt);
- }
- TNode TNode::Node(TZtStringBuf xpath, bool quiet, TXPathContext& ctxt) {
- TConstNodes n = Nodes(xpath, quiet, ctxt);
- if (n.Size() == 0 && !quiet)
- THROW(NodeNotFound, xpath);
- if (n.Size() == 0)
- return TNode();
- else
- return n[0].ConstCast();
- }
- TConstNode TNode::Node(TZtStringBuf xpath, bool quiet, TXPathContext& ctxt) const {
- return const_cast<TNode*>(this)->Node(xpath, quiet, ctxt);
- }
- TNode TNode::FirstChild(TZtStringBuf name) {
- if (IsNull())
- THROW(XmlException, "Node is null");
- return Find(NodePointer->children, name);
- }
- TConstNode TNode::FirstChild(TZtStringBuf name) const {
- return const_cast<TNode*>(this)->FirstChild(name);
- }
- TNode TNode::FirstChild() {
- if (IsNull())
- THROW(XmlException, "Node is null");
- return TNode(DocPointer, NodePointer->children);
- }
- TConstNode TNode::FirstChild() const {
- return const_cast<TNode*>(this)->FirstChild();
- }
- TNode TNode::Parent() {
- if (nullptr == NodePointer->parent)
- THROW(XmlException, "Parent node not exists");
- return TNode(DocPointer, NodePointer->parent);
- }
- TConstNode TNode::Parent() const {
- return const_cast<TNode*>(this)->Parent();
- }
- TNode TNode::NextSibling(TZtStringBuf name) {
- if (IsNull())
- THROW(XmlException, "Node is null");
- return Find(NodePointer->next, name);
- }
- TConstNode TNode::NextSibling(TZtStringBuf name) const {
- return const_cast<TNode*>(this)->NextSibling(name);
- }
- TNode TNode::NextSibling() {
- if (IsNull())
- THROW(XmlException, "Node is null");
- return TNode(DocPointer, NodePointer->next);
- }
- TConstNode TNode::NextSibling() const {
- return const_cast<TNode*>(this)->NextSibling();
- }
- /* NOTE: by default child will inherit it's parent ns */
- TNode TNode::AddChild(TZtStringBuf name) {
- return AddChild(name, "");
- }
- /* NOTE: source node will be copied, as otherwise it will be double-freed from this and its own document */
- TNode TNode::AddChild(const TConstNode& node) {
- xmlNodePtr copy = xmlDocCopyNode(node.ConstCast().NodePointer, DocPointer, 1 /* recursive */);
- copy = xmlAddChild(NodePointer, copy);
- return TNode(DocPointer, copy);
- }
- void TNode::SetPrivate(void* priv) {
- NodePointer->_private = priv;
- }
- void* TNode::GetPrivate() const {
- return NodePointer->_private;
- }
- TNode TNode::Find(xmlNode* start, TZtStringBuf name) {
- for (; start; start = start->next)
- if (start->type == XML_ELEMENT_NODE && (name.empty() || !xmlStrcmp(start->name, XMLCHAR(name.c_str()))))
- return TNode(DocPointer, start);
- return TNode();
- }
- TString TNode::Name() const {
- if (IsNull())
- THROW(XmlException, "Node is null");
- return CAST2CHAR(NodePointer->name);
- }
- TString TNode::Path() const {
- TCharPtr path(xmlGetNodePath(NodePointer));
- if (!!path)
- return CAST2CHAR(path.Get());
- else
- return "";
- }
- xmlNode* TNode::GetPtr() {
- return NodePointer;
- }
- const xmlNode* TNode::GetPtr() const {
- return NodePointer;
- }
- bool TNode::IsText() const {
- if (IsNull())
- THROW(XmlException, "Node is null");
- return NodePointer->type == XML_TEXT_NODE;
- }
- void TNode::Remove() {
- xmlNode* nodePtr = GetPtr();
- xmlUnlinkNode(nodePtr);
- xmlFreeNode(nodePtr);
- }
- static int XmlWriteToOstream(void* context, const char* buffer, int len) {
- // possibly use to save doc as well
- IOutputStream* out = (IOutputStream*)context;
- out->Write(buffer, len);
- return len;
- }
- void TNode::SaveInternal(IOutputStream& stream, TZtStringBuf enc, int options) const {
- const char* encoding = enc.size() ? enc.data() : "utf-8";
- TSaveCtxtPtr ctx(xmlSaveToIO(XmlWriteToOstream, /* close */ nullptr, &stream,
- encoding, options));
- if (xmlSaveTree(ctx.Get(), (xmlNode*)GetPtr()) < 0)
- THROW(XmlException, "Failed saving node to stream");
- }
- void TNode::Save(IOutputStream& stream, TZtStringBuf enc, bool shouldFormat) const {
- SaveInternal(stream, enc, shouldFormat ? XML_SAVE_FORMAT : 0);
- }
- void TNode::SaveAsHtml(IOutputStream& stream, TZtStringBuf enc, bool shouldFormat) const {
- int options = XML_SAVE_AS_HTML;
- options |= shouldFormat ? XML_SAVE_FORMAT : 0;
- SaveInternal(stream, enc, options);
- }
- TConstNodes::TConstNodes(const TConstNodes& nodes)
- : SizeValue(nodes.Size())
- , Doc(nodes.Doc)
- , Obj(nodes.Obj)
- {
- }
- TConstNodes& TConstNodes::operator=(const TConstNodes& nodes) {
- if (this != &nodes) {
- SizeValue = nodes.Size();
- Doc = nodes.Doc;
- Obj = nodes.Obj;
- }
- return *this;
- }
- TConstNodes::TConstNodes(TConstNodesRef ref)
- : SizeValue(ref.r_.Size())
- , Doc(ref.r_.Doc)
- , Obj(ref.r_.Obj)
- {
- }
- TConstNodes& TConstNodes::operator=(TConstNodesRef ref) {
- if (this != &ref.r_) {
- SizeValue = ref.r_.Size();
- Doc = ref.r_.Doc;
- Obj = ref.r_.Obj;
- }
- return *this;
- }
- TConstNodes::operator TConstNodesRef() {
- return TConstNodesRef(*this);
- }
- TConstNodes::TConstNodes(xmlDoc* doc, TXPathObjectPtr obj)
- : SizeValue(obj && obj->nodesetval ? obj->nodesetval->nodeNr : 0)
- , Doc(doc)
- , Obj(obj)
- {
- }
- TConstNode TConstNodes::operator[](size_t number) const {
- if (number + 1 > Size())
- THROW(XmlException, "index out of range " << number);
- if (!Obj || !Obj->nodesetval)
- THROW(XmlException, "Broken TConstNodes object, Obj is null");
- xmlNode* node = Obj->nodesetval->nodeTab[number];
- return TNode(Doc, node);
- }
- TConstNode TConstNodes::TNodeIter::operator*() const {
- return Nodes[Index];
- }
- }
|