Imported Upstream version 2.91.2
[platform/upstream/libxml++.git] / libxml++ / validators / dtdvalidator.cc
1 /* dtdvalidator.cpp
2  * libxml++ and this file are copyright (C) 2000 by Ari Johnson
3  * (C) 2002-2004 by the libxml dev team and
4  * are covered by the GNU Lesser General Public License, which should be
5  * included with libxml++ as the file COPYING.
6  */
7
8 #include "libxml++/validators/dtdvalidator.h"
9 #include "libxml++/dtd.h"
10 #include "libxml++/nodes/node.h"
11 #include "libxml++/exceptions/internal_error.h"
12 #include "libxml++/exceptions/validity_error.h"
13 #include "libxml++/io/istreamparserinputbuffer.h"
14 #include "libxml++/document.h"
15
16 #include <libxml/parser.h>
17
18 #include <sstream>
19 #include <iostream>
20
21 namespace xmlpp
22 {
23
24 struct DtdValidator::Impl
25 {
26   Impl() : dtd(nullptr), is_dtd_owner(false), context(nullptr) {}
27
28   Dtd* dtd;
29   bool is_dtd_owner;
30   _xmlValidCtxt* context;
31 };
32
33
34 DtdValidator::DtdValidator()
35 : pimpl_(new Impl)
36 {
37 }
38
39 DtdValidator::DtdValidator(const std::string& filename)
40 : pimpl_(new Impl)
41 {
42   parse_file(filename);
43 }
44
45 DtdValidator::DtdValidator(const Glib::ustring& external, const Glib::ustring& system)
46 : pimpl_(new Impl)
47 {
48   parse_subset(external, system);
49 }
50
51 DtdValidator::DtdValidator(Dtd* dtd, bool take_ownership)
52 : pimpl_(new Impl)
53 {
54   set_dtd(dtd, take_ownership);
55 }
56
57 DtdValidator::~DtdValidator()
58 {
59   release_underlying();
60 }
61
62 void DtdValidator::parse_file(const std::string& filename)
63 {
64   set_dtd(new Dtd(filename), true);
65 }
66
67 void DtdValidator::parse_subset(const Glib::ustring& external, const Glib::ustring& system)
68 {
69   set_dtd(new Dtd(external, system), true);
70 }
71
72 void DtdValidator::parse_memory(const Glib::ustring& contents)
73 {
74   std::unique_ptr<Dtd> dtd(new Dtd());
75   dtd->parse_memory(contents);
76   set_dtd(dtd.release(), true);
77 }
78
79 void DtdValidator::parse_stream(std::istream& in)
80 {
81   std::unique_ptr<Dtd> dtd(new Dtd());
82   dtd->parse_stream(in);
83   set_dtd(dtd.release(), true);
84 }
85
86 void DtdValidator::set_dtd(Dtd* dtd, bool take_ownership)
87 {
88   release_underlying();
89   pimpl_->dtd = dtd;
90   pimpl_->is_dtd_owner = take_ownership;
91 }
92
93 void DtdValidator::initialize_context()
94 {
95   Validator::initialize_context();
96
97   if (pimpl_->context)
98   {
99     //Tell the validation context about the callbacks:
100     pimpl_->context->error = &callback_validity_error;
101     pimpl_->context->warning = &callback_validity_warning;
102
103     //Allow the callback_validity_*() methods to retrieve the C++ instance:
104     pimpl_->context->userData = this;
105   }
106 }
107
108 void DtdValidator::release_underlying()
109 {
110   if (pimpl_->context)
111   {
112     pimpl_->context->userData = nullptr; //Not really necessary.
113
114     xmlFreeValidCtxt(pimpl_->context);
115     pimpl_->context = nullptr;
116   }
117
118   if (pimpl_->dtd)
119   {
120     if (pimpl_->is_dtd_owner)
121       delete pimpl_->dtd;
122     pimpl_->dtd = nullptr;
123   }
124
125   Validator::release_underlying();
126 }
127
128 DtdValidator::operator bool() const noexcept
129 {
130   return pimpl_->dtd && pimpl_->dtd->cobj();
131 }
132
133 Dtd* DtdValidator::get_dtd()
134 {
135   return pimpl_->dtd;
136 }
137
138 const Dtd* DtdValidator::get_dtd() const
139 {
140   return pimpl_->dtd;
141 }
142
143 void DtdValidator::validate(const Document* document)
144 {
145   if (!document)
146   {
147     throw internal_error("Document pointer cannot be nullptr.");
148   }
149
150   if (!pimpl_->dtd)
151   {
152     throw internal_error("No DTD to use for validation.");
153   }
154
155   // A context is required at this stage only
156   if (!pimpl_->context)
157     pimpl_->context = xmlNewValidCtxt();
158
159   if (!pimpl_->context)
160   {
161     throw internal_error("Couldn't create validation context");
162   }
163
164   xmlResetLastError();
165   initialize_context();
166
167   const bool res = (bool)xmlValidateDtd(pimpl_->context, (xmlDoc*)document->cobj(),
168                    pimpl_->dtd->cobj());
169
170   if (!res)
171   {
172     check_for_exception();
173     throw validity_error("Document failed DTD validation\n" + format_xml_error());
174   }
175 }
176
177 } // namespace xmlpp