Update copyright.
[shibboleth/cpp-xmltooling.git] / xmltooling / AbstractDOMCachingXMLObject.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * AbstractDOMCachingXMLObject.cpp
19  * 
20  * Extension of AbstractXMLObject that implements a DOMCachingXMLObject. 
21  */
22
23 #include "internal.h"
24 #include "AbstractDOMCachingXMLObject.h"
25 #include "exceptions.h"
26 #include "XMLObjectBuilder.h"
27 #include "util/XMLHelper.h"
28
29 #include <algorithm>
30 #include <functional>
31
32 using namespace xmltooling;
33 using namespace std;
34
35 AbstractDOMCachingXMLObject::~AbstractDOMCachingXMLObject()
36 {
37     if (m_document)
38         m_document->release();
39 }
40
41 void AbstractDOMCachingXMLObject::setDOM(DOMElement* dom, bool bindDocument) const
42 {
43     m_dom=dom;
44     if (dom) {
45         if (bindDocument) {
46             setDocument(dom->getOwnerDocument());
47         }
48     }
49 }
50
51 void AbstractDOMCachingXMLObject::releaseDOM() const
52 {
53     if (m_dom) {
54         if (m_log.isDebugEnabled()) {
55             string qname=getElementQName().toString();
56             m_log.debug("releasing cached DOM representation for (%s)", qname.empty() ? "unknown" : qname.c_str());
57         }
58         setDOM(NULL);
59     }
60 }
61
62 void AbstractDOMCachingXMLObject::releaseParentDOM(bool propagateRelease) const
63 {
64     if (getParent() && getParent()->getDOM()) {
65         m_log.debug(
66             "releasing cached DOM representation for parent object with propagation set to %s",
67             propagateRelease ? "true" : "false"
68             );
69         getParent()->releaseDOM();
70         if (propagateRelease)
71             getParent()->releaseParentDOM(propagateRelease);
72     }
73 }
74
75 class _release : public binary_function<XMLObject*,bool,void> {
76 public:
77     void operator()(XMLObject* obj, bool propagate) const {
78         if (obj) {
79             obj->releaseDOM();
80             if (propagate)
81                 obj->releaseChildrenDOM(propagate);
82         }
83     }
84 };
85
86 void AbstractDOMCachingXMLObject::releaseChildrenDOM(bool propagateRelease) const
87 {
88     if (hasChildren()) {
89         m_log.debug(
90             "releasing cached DOM representation for children with propagation set to %s",
91             propagateRelease ? "true" : "false"
92             );
93         const list<XMLObject*>& children=getOrderedChildren();
94         for_each(children.begin(),children.end(),bind2nd(_release(),propagateRelease));
95     }
96 }
97
98 DOMElement* AbstractDOMCachingXMLObject::cloneDOM(DOMDocument* doc) const
99 {
100     if (getDOM()) {
101         if (!doc)
102             doc=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();
103         return static_cast<DOMElement*>(doc->importNode(getDOM(),true));
104     }
105     return NULL;
106 }
107
108 XMLObject* AbstractDOMCachingXMLObject::clone() const
109 {
110     // See if we can clone via the DOM.
111     DOMElement* domCopy=cloneDOM();
112     if (domCopy) {
113         // Seemed to work, so now we unmarshall the DOM to produce the clone.
114         const XMLObjectBuilder* b=XMLObjectBuilder::getBuilder(domCopy);
115         if (!b) {
116             auto_ptr<QName> q(XMLHelper::getNodeQName(domCopy));
117             m_log.error(
118                 "DOM clone failed, unable to locate builder for element (%s)", q->toString().c_str()
119                 );
120             domCopy->getOwnerDocument()->release();
121             throw UnmarshallingException("Unable to locate builder for cloned element.");
122         }
123         XercesJanitor<DOMDocument> janitor(domCopy->getOwnerDocument());
124         XMLObject* ret = b->buildFromElement(domCopy,true); // bind document
125         janitor.release(); // safely transferred
126         return ret;
127     }
128     return NULL;
129 }
130
131 void AbstractDOMCachingXMLObject::detach()
132 {
133     // This is an override that duplicates some of the checking in the base class but
134     // adds document management in preparation for deletion of the parent.
135
136     if (!getParent())
137         return;
138
139     if (getParent()->hasParent())
140         throw XMLObjectException("Cannot detach an object whose parent is itself a child.");
141
142     AbstractDOMCachingXMLObject* parent = dynamic_cast<AbstractDOMCachingXMLObject*>(getParent());
143     if (parent && parent->m_document) {
144         // Transfer control of document to me...
145         setDocument(parent->m_document);
146         parent->m_document = NULL;
147     }
148     // The rest is done by the base.
149     AbstractXMLObject::detach();
150 }