9af4b26708c77b7fa0c7249b3ab33862ef911333
[shibboleth/cpp-xmltooling.git] / xmltooling / impl / UnknownElement.cpp
1 /*\r
2 *  Copyright 2001-2006 Internet2\r
3  * \r
4 * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * UnknownElement.cpp\r
19  * \r
20  * Basic implementations suitable for use as defaults for unrecognized content\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "exceptions.h"\r
25 #include "impl/UnknownElement.h"\r
26 #include "util/NDC.h"\r
27 \r
28 #include <log4cpp/Category.hh>\r
29 #include <xercesc/framework/MemBufFormatTarget.hpp>\r
30 #include <xercesc/framework/MemBufInputSource.hpp>\r
31 #include <xercesc/framework/Wrapper4InputSource.hpp>\r
32 #include <xercesc/util/XMLUniDefs.hpp>\r
33 \r
34 using namespace xmltooling;\r
35 using namespace log4cpp;\r
36 using namespace std;\r
37 \r
38 void UnknownElementImpl::releaseDOM()\r
39 {\r
40 #ifdef _DEBUG\r
41     xmltooling::NDC ndc("releaseDOM");\r
42 #endif\r
43     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".UnknownElementImpl");\r
44     log.debug("releasing DOM for unknown content, preserving current DOM in XML form");\r
45 \r
46     // We're losing our DOM, so assuming we have one, we preserve it.\r
47     serialize(m_xml);\r
48 \r
49     // This takes care of the generic housekeeping now that we've preserved things.\r
50     AbstractDOMCachingXMLObject::releaseDOM();\r
51 }\r
52 \r
53 XMLObject* UnknownElementImpl::clone() const\r
54 {\r
55     UnknownElementImpl* ret=new UnknownElementImpl();\r
56 \r
57     // If there's no XML locally, serialize this object into the new one.\r
58     // Otherwise just copy it over.\r
59     if (m_xml.empty())\r
60         serialize(ret->m_xml);\r
61     else\r
62         ret->m_xml=m_xml;\r
63 \r
64     return ret;\r
65 }\r
66 \r
67 void UnknownElementImpl::serialize(string& s) const\r
68 {\r
69     if (getDOM()) {\r
70         static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };\r
71         static const XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDigit_8, chNull };\r
72         DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);\r
73         DOMWriter* serializer=(static_cast<DOMImplementationLS*>(impl))->createDOMWriter();\r
74         serializer->setEncoding(UTF8);\r
75         try {\r
76             MemBufFormatTarget target;\r
77             if (!serializer->writeNode(&target,*(getDOM())))\r
78                 throw XMLObjectException("unable to serialize XML to preserve DOM");\r
79             s.erase();\r
80             s.append(reinterpret_cast<const char*>(target.getRawBuffer()),target.getLen());\r
81             serializer->release();\r
82         }\r
83         catch (...) {\r
84             serializer->release();\r
85             throw;\r
86         }\r
87     }\r
88 }\r
89 \r
90 DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document) const\r
91 {\r
92 #ifdef _DEBUG\r
93     xmltooling::NDC ndc("marshall");\r
94 #endif\r
95     \r
96     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Marshaller");\r
97     log.debug("marshalling unknown content");\r
98 \r
99     UnknownElementImpl* unk=dynamic_cast<UnknownElementImpl*>(xmlObject);\r
100     if (!unk)\r
101         throw MarshallingException("Only objects of class UnknownElementImpl can be marshalled.");\r
102     \r
103     DOMElement* cachedDOM=unk->getDOM();\r
104     if (cachedDOM) {\r
105         if (!document || document==cachedDOM->getOwnerDocument()) {\r
106             log.debug("XMLObject has a usable cached DOM, reusing it");\r
107             setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);\r
108             unk->releaseParentDOM(true);\r
109             return cachedDOM;\r
110         }\r
111         \r
112         // We have a DOM but it doesn't match the document we were given. This both sucks and blows.\r
113         // Without an adoptNode option to maintain the child pointers, we rely on our custom\r
114         // implementation class to preserve the XML when we release the existing DOM.\r
115         unk->releaseDOM();\r
116     }\r
117     \r
118     // If we get here, we didn't have a usable DOM (and/or we flushed the one we had).\r
119     // We need to reparse the XML we saved off into a new DOM.\r
120     bool bindDocument=false;\r
121     MemBufInputSource src(reinterpret_cast<const XMLByte*>(unk->m_xml.c_str()),unk->m_xml.length(),"UnknownElementImpl");\r
122     Wrapper4InputSource dsrc(&src,false);\r
123     log.debug("parsing XML back into DOM tree");\r
124     DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc);\r
125     if (document) {\r
126         // The caller insists on using his own document, so we now have to import the thing\r
127         // into it. Then we're just dumping the one we built.\r
128         log.debug("reimporting new DOM into caller-supplied document");\r
129         cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));\r
130         internalDoc->release();\r
131     }\r
132     else {\r
133         // We just bind the document we built to the object as the result.\r
134         cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());\r
135         document=internalDoc;\r
136         bindDocument=true;\r
137     }\r
138 \r
139     // Recache the DOM and clear the serialized copy.\r
140     setDocumentElement(document, cachedDOM);\r
141     log.debug("caching DOM for XMLObject (document is %sbound)", bindDocument ? "" : "not ");\r
142     unk->setDOM(cachedDOM, bindDocument);\r
143     unk->releaseParentDOM(true);\r
144     unk->m_xml.erase();\r
145     return cachedDOM;\r
146 }\r
147 \r
148 DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement) const\r
149 {\r
150 #ifdef _DEBUG\r
151     xmltooling::NDC ndc("marshall");\r
152 #endif\r
153     \r
154     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Marshaller");\r
155     log.debug("marshalling unknown content");\r
156 \r
157     UnknownElementImpl* unk=dynamic_cast<UnknownElementImpl*>(xmlObject);\r
158     if (!unk)\r
159         throw MarshallingException("Only objects of class UnknownElementImpl can be marshalled.");\r
160     \r
161     DOMElement* cachedDOM=unk->getDOM();\r
162     if (cachedDOM) {\r
163         if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {\r
164             log.debug("XMLObject has a usable cached DOM, reusing it");\r
165             parentElement->appendChild(cachedDOM);\r
166             unk->releaseParentDOM(true);\r
167             return cachedDOM;\r
168         }\r
169         \r
170         // We have a DOM but it doesn't match the document we were given. This both sucks and blows.\r
171         // Without an adoptNode option to maintain the child pointers, we rely on our custom\r
172         // implementation class to preserve the XML when we release the existing DOM.\r
173         unk->releaseDOM();\r
174     }\r
175     \r
176     // If we get here, we didn't have a usable DOM (and/or we flushed the one we had).\r
177     // We need to reparse the XML we saved off into a new DOM.\r
178     MemBufInputSource src(reinterpret_cast<const XMLByte*>(unk->m_xml.c_str()),unk->m_xml.length(),"UnknownElementImpl");\r
179     Wrapper4InputSource dsrc(&src,false);\r
180     log.debug("parsing XML back into DOM tree");\r
181     DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc);\r
182     \r
183     log.debug("reimporting new DOM into caller-supplied document");\r
184     cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(), true));\r
185     internalDoc->release();\r
186 \r
187     // Recache the DOM and clear the serialized copy.\r
188     parentElement->appendChild(cachedDOM);\r
189     log.debug("caching DOM for XMLObject");\r
190     unk->setDOM(cachedDOM, false);\r
191     unk->releaseParentDOM(true);\r
192     unk->m_xml.erase();\r
193     return cachedDOM;\r
194 }\r
195 \r
196 XMLObject* UnknownElementUnmarshaller::unmarshall(DOMElement* element, bool bindDocument) const\r
197 {\r
198     UnknownElementImpl* ret=new UnknownElementImpl();\r
199     ret->setDOM(element, bindDocument);\r
200     return ret;\r
201 }\r