Merge branch '1.x' of ssh://authdev.it.ohio-state.edu/~scantor/git/cpp-xmltooling...
[shibboleth/cpp-xmltooling.git] / xmltooling / impl / UnknownElement.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * UnknownElement.cpp
23  * 
24  * Basic implementation suitable for use as default for unrecognized content.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "logging.h"
30 #include "impl/UnknownElement.h"
31 #include "util/NDC.h"
32 #include "util/XMLHelper.h"
33
34 #include <xercesc/framework/MemBufInputSource.hpp>
35 #include <xercesc/framework/Wrapper4InputSource.hpp>
36 #include <xercesc/util/XMLUniDefs.hpp>
37
38 using namespace xmltooling::logging;
39 using namespace xmltooling;
40 using namespace xercesc;
41 using namespace std;
42 #ifndef XMLTOOLING_NO_XMLSEC
43 using xmlsignature::Signature;
44 #endif
45
46 UnknownElementImpl::UnknownElementImpl(const XMLCh* namespaceURI, const XMLCh* elementLocalName, const XMLCh* namespacePrefix)
47     : AbstractXMLObject(namespaceURI, elementLocalName, namespacePrefix)
48 {
49 }
50
51 UnknownElementImpl::~UnknownElementImpl()
52 {
53 }
54
55 void UnknownElementImpl::releaseDOM() const
56 {
57 #ifdef _DEBUG
58     xmltooling::NDC ndc("releaseDOM");
59 #endif
60     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject");
61     log.debug("releasing DOM for unknown content, preserving current DOM in XML form");
62
63     // We're losing our DOM, so assuming we have one, we preserve it.
64     serialize(m_xml);
65
66     // This takes care of the generic housekeeping now that we've preserved things.
67     AbstractDOMCachingXMLObject::releaseDOM();
68 }
69
70 XMLObject* UnknownElementImpl::clone() const
71 {
72     UnknownElementImpl* ret=new UnknownElementImpl();
73
74     // If there's no XML locally, serialize this object into the new one.
75     // Otherwise just copy it over.
76     if (m_xml.empty())
77         serialize(ret->m_xml);
78     else
79         ret->m_xml=m_xml;
80
81     return ret;
82 }
83
84 const XMLCh* UnknownElementImpl::getTextContent(unsigned int position) const
85 {
86     throw XMLObjectException("Direct access to content is not permitted.");
87 }
88
89 void UnknownElementImpl::setTextContent(const XMLCh*, unsigned int position)
90 {
91     throw XMLObjectException("Direct access to content is not permitted.");
92 }
93
94 void UnknownElementImpl::setDocumentElement(DOMDocument* document, DOMElement* element) const
95 {
96     DOMElement* documentRoot = document->getDocumentElement();
97     if (documentRoot)
98         document->replaceChild(element, documentRoot);
99     else
100         document->appendChild(element);
101 }
102
103 void UnknownElementImpl::serialize(string& s) const
104 {
105     if (getDOM())
106         XMLHelper::serialize(getDOM(),s);
107 }
108
109 DOMElement* UnknownElementImpl::marshall(
110     DOMDocument* document
111 #ifndef XMLTOOLING_NO_XMLSEC
112     ,const vector<Signature*>* sigs
113     ,const Credential* credential
114 #endif
115     ) const
116 {
117 #ifdef _DEBUG
118     xmltooling::NDC ndc("marshall");
119 #endif
120     
121     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject");
122     log.debug("marshalling unknown content");
123
124     DOMElement* cachedDOM=getDOM();
125     if (cachedDOM) {
126         if (!document || document==cachedDOM->getOwnerDocument()) {
127             log.debug("XMLObject has a usable cached DOM, reusing it");
128             if (document)
129                 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
130             releaseParentDOM(true);
131             return cachedDOM;
132         }
133         
134         // We have a DOM but it doesn't match the document we were given, so we import
135         // it into the new document.
136         try {
137             cachedDOM=static_cast<DOMElement*>(document->importNode(cachedDOM, true));
138         }
139         catch (XMLException& ex) {
140             auto_ptr_char temp(ex.getMessage());
141             throw XMLParserException(
142                 string("Error importing DOM into caller-supplied document: ") + (temp.get() ? temp.get() : "no message")
143                 );
144         }
145
146         // Recache the DOM.
147         setDocumentElement(document, cachedDOM);
148         log.debug("caching imported DOM for XMLObject");
149         setDOM(cachedDOM, false);
150         releaseParentDOM(true);
151         return cachedDOM;
152     }
153     
154     // If we get here, we didn't have a usable DOM.
155     // We need to reparse the XML we saved off into a new DOM.
156     bool bindDocument=false;
157     MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"UnknownElementImpl");
158     Wrapper4InputSource dsrc(&src,false);
159     log.debug("parsing XML back into DOM tree");
160     DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
161     if (document) {
162         // The caller insists on using his own document, so we now have to import the thing
163         // into it. Then we're just dumping the one we built.
164         log.debug("reimporting new DOM into caller-supplied document");
165         try {
166             cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));
167         }
168         catch (XMLException& ex) {
169             internalDoc->release();
170             auto_ptr_char temp(ex.getMessage());
171             throw XMLParserException(
172                 string("Error importing DOM into caller-supplied document: ") + (temp.get() ? temp.get() : "no message")
173                 );
174         }
175         internalDoc->release();
176     }
177     else {
178         // We just bind the document we built to the object as the result.
179         cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());
180         document=internalDoc;
181         bindDocument=true;
182     }
183
184     // Recache the DOM and clear the serialized copy.
185     setDocumentElement(document, cachedDOM);
186     log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");
187     setDOM(cachedDOM, bindDocument);
188     releaseParentDOM(true);
189     m_xml.erase();
190     return cachedDOM;
191 }
192
193
194 DOMElement* UnknownElementImpl::marshall(
195     DOMElement* parentElement
196 #ifndef XMLTOOLING_NO_XMLSEC
197     ,const vector<Signature*>* sigs
198     ,const Credential* credential
199 #endif
200     ) const
201 {
202 #ifdef _DEBUG
203     xmltooling::NDC ndc("marshall");
204 #endif
205     
206     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject");
207     log.debug("marshalling unknown content");
208
209     DOMElement* cachedDOM=getDOM();
210     if (cachedDOM) {
211         if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
212             log.debug("XMLObject has a usable cached DOM, reusing it");
213             parentElement->appendChild(cachedDOM);
214             releaseParentDOM(true);
215             return cachedDOM;
216         }
217         
218         // We have a DOM but it doesn't match the document we were given, so we import
219         // it into the new document.
220         try {
221             cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(cachedDOM, true));
222         }
223         catch (XMLException& ex) {
224             auto_ptr_char temp(ex.getMessage());
225             throw XMLParserException(
226                 string("Error importing DOM into caller-supplied document: ") + (temp.get() ? temp.get() : "no message")
227                 );
228         }
229
230         // Recache the DOM.
231         parentElement->appendChild(cachedDOM);
232         log.debug("caching imported DOM for XMLObject");
233         setDOM(cachedDOM, false);
234         releaseParentDOM(true);
235         return cachedDOM;
236     }
237     
238     // If we get here, we didn't have a usable DOM (and/or we flushed the one we had).
239     // We need to reparse the XML we saved off into a new DOM.
240     MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"UnknownElementImpl");
241     Wrapper4InputSource dsrc(&src,false);
242     log.debug("parsing XML back into DOM tree");
243     DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
244     
245     log.debug("reimporting new DOM into caller-supplied document");
246     try {
247         cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(), true));
248     }
249     catch (XMLException& ex) {
250         internalDoc->release();
251         auto_ptr_char temp(ex.getMessage());
252         throw XMLParserException(
253             string("Error importing DOM into caller-supplied document: ") + (temp.get() ? temp.get() : "no message")
254             );
255     }
256     internalDoc->release();
257
258     // Recache the DOM and clear the serialized copy.
259     parentElement->appendChild(cachedDOM);
260     log.debug("caching DOM for XMLObject");
261     setDOM(cachedDOM, false);
262     releaseParentDOM(true);
263     m_xml.erase();
264     return cachedDOM;
265 }
266
267 XMLObject* UnknownElementImpl::unmarshall(DOMElement* element, bool bindDocument)
268 {
269     setDOM(element, bindDocument);
270     return this;
271 }
272
273 XMLObject* UnknownElementBuilder::buildObject(
274     const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const xmltooling::QName* schemaType
275     ) const
276 {
277     return new UnknownElementImpl(nsURI,localName,prefix);
278 }
279