Reducing header overuse, non-inlining selected methods (CPPOST-35).
[shibboleth/cpp-xmltooling.git] / xmltooling / util / XMLHelper.cpp
1 /*
2  *  Copyright 2001-2009 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  * XMLHelper.cpp
19  * 
20  * A helper class for working with W3C DOM objects. 
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "QName.h"
26 #include "XMLObject.h"
27 #include "util/XMLHelper.h"
28 #include "util/XMLConstants.h"
29
30 #include <xercesc/framework/MemBufFormatTarget.hpp>
31 #include <xercesc/util/XMLUniDefs.hpp>
32
33 using namespace xmltooling;
34 using namespace xercesc;
35 using namespace std;
36
37 static const XMLCh type[]={chLatin_t, chLatin_y, chLatin_p, chLatin_e, chNull };
38     
39 bool XMLHelper::hasXSIType(const DOMElement* e)
40 {
41     if (e) {
42         if (e->hasAttributeNS(xmlconstants::XSI_NS, type)) {
43             return true;
44         }
45     }
46
47     return false;
48 }
49
50 xmltooling::QName* XMLHelper::getXSIType(const DOMElement* e)
51 {
52     DOMAttr* attribute = e->getAttributeNodeNS(xmlconstants::XSI_NS, type);
53     if (attribute) {
54         const XMLCh* attributeValue = attribute->getTextContent();
55         if (attributeValue && *attributeValue) {
56             int i;
57             if ((i=XMLString::indexOf(attributeValue,chColon))>0) {
58                 XMLCh* prefix=new XMLCh[i+1];
59                 XMLString::subString(prefix,attributeValue,0,i);
60                 prefix[i]=chNull;
61                 xmltooling::QName* ret=new xmltooling::QName(e->lookupNamespaceURI(prefix), attributeValue + i + 1, prefix);
62                 delete[] prefix;
63                 return ret;
64             }
65             else {
66                 return new xmltooling::QName(e->lookupNamespaceURI(NULL), attributeValue);
67             }
68         }
69     }
70
71     return NULL;
72 }
73
74 DOMAttr* XMLHelper::getIdAttribute(const DOMElement* domElement)
75 {
76     if(!domElement->hasAttributes()) {
77         return NULL;
78     }
79     
80     DOMNamedNodeMap* attributes = domElement->getAttributes();
81     DOMAttr* attribute;
82     for(XMLSize_t i = 0; i < attributes->getLength(); i++) {
83         attribute = static_cast<DOMAttr*>(attributes->item(i));
84         if(attribute->isId()) {
85             return attribute;
86         }
87     }
88     
89     return NULL;
90 }
91
92 const XMLObject* XMLHelper::getXMLObjectById(const XMLObject& tree, const XMLCh* id)
93 {
94     if (XMLString::equals(id, tree.getXMLID()))
95         return &tree;
96     
97     const XMLObject* ret;
98     const list<XMLObject*>& children = tree.getOrderedChildren();
99     for (list<XMLObject*>::const_iterator i=children.begin(); i!=children.end(); ++i) {
100         if (*i) {
101             ret = getXMLObjectById(*(*i), id);
102             if (ret)
103                 return ret;
104         }
105     }
106     
107     return NULL;
108 }
109
110 XMLObject* XMLHelper::getXMLObjectById(XMLObject& tree, const XMLCh* id)
111 {
112     if (XMLString::equals(id, tree.getXMLID()))
113         return &tree;
114     
115     XMLObject* ret;
116     const list<XMLObject*>& children = tree.getOrderedChildren();
117     for (list<XMLObject*>::const_iterator i=children.begin(); i!=children.end(); ++i) {
118         if (*i) {
119             ret = getXMLObjectById(*(*i), id);
120             if (ret)
121                 return ret;
122         }
123     }
124     
125     return NULL;
126 }
127
128 xmltooling::QName* XMLHelper::getNodeQName(const DOMNode* domNode)
129 {
130     if (domNode)
131         return new xmltooling::QName(domNode->getNamespaceURI(), domNode->getLocalName(), domNode->getPrefix());
132     return NULL; 
133 }
134
135 xmltooling::QName* XMLHelper::getAttributeValueAsQName(const DOMAttr* attribute)
136 {
137     return getNodeValueAsQName(attribute);
138 }
139
140 xmltooling::QName* XMLHelper::getNodeValueAsQName(const DOMNode* domNode)
141 {
142     if (!domNode)
143         return NULL;
144     
145     int i;
146     const XMLCh* value=domNode->getTextContent();
147     if (value && (i=XMLString::indexOf(value,chColon))>0) {
148         XMLCh* prefix=new XMLCh[i+1];
149         XMLString::subString(prefix,value,0,i);
150         prefix[i]=chNull;
151         xmltooling::QName* ret=new xmltooling::QName(domNode->lookupNamespaceURI(prefix), value + i + 1, prefix);
152         delete[] prefix;
153         return ret;
154     }
155     
156     return new xmltooling::QName(domNode->lookupNamespaceURI(NULL), value);
157 }
158
159 DOMElement* XMLHelper::appendChildElement(DOMElement* parentElement, DOMElement* childElement)
160 {
161     DOMDocument* parentDocument = parentElement->getOwnerDocument();
162     if (childElement->getOwnerDocument() != parentDocument) {
163         childElement = static_cast<DOMElement*>(parentDocument->importNode(childElement, true));
164     }
165
166     parentElement->appendChild(childElement);
167     return childElement;
168 }
169
170 const XMLCh* XMLHelper::getTextContent(const DOMElement* e)
171 {
172     DOMNode* child=e->getFirstChild();
173     while (child) {
174         if (child->getNodeType()==DOMNode::TEXT_NODE)
175             return child->getNodeValue();
176         child=child->getNextSibling();
177     }
178     return NULL;
179 }
180
181 DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* localName)
182 {
183     DOMNode* child = n->getFirstChild();
184     while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)
185         child = child->getNextSibling();
186     if (child && localName) {
187         if (!XMLString::equals(localName,child->getLocalName()))
188             return getNextSiblingElement(child, localName);
189     }
190     return static_cast<DOMElement*>(child);
191 }    
192
193 DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* localName)
194 {
195     DOMNode* child = n->getLastChild();
196     while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)
197         child = child->getPreviousSibling();
198     if (child && localName) {
199         if (!XMLString::equals(localName,child->getLocalName()))
200             return getPreviousSiblingElement(child, localName);
201     }
202     return static_cast<DOMElement*>(child);
203 }    
204
205 DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
206 {
207     DOMElement* e = getFirstChildElement(n, localName);
208     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
209         e = getNextSiblingElement(e, localName);
210     return e;
211 }
212
213 DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
214 {
215     DOMElement* e = getLastChildElement(n, localName);
216     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
217         e = getPreviousSiblingElement(e, localName);
218     return e;
219 }
220
221 DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* localName)
222 {
223     DOMNode* sib = n->getNextSibling();
224     while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)
225         sib = sib->getNextSibling();
226     if (sib && localName) {
227         if (!XMLString::equals(localName,sib->getLocalName()))
228             return getNextSiblingElement(sib, localName);
229     }   
230     return static_cast<DOMElement*>(sib);
231 }
232
233 DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* localName)
234 {
235     DOMNode* sib = n->getPreviousSibling();
236     while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)
237         sib = sib->getPreviousSibling();
238     if (sib && localName) {
239         if (!XMLString::equals(localName,sib->getLocalName()))
240             return getPreviousSiblingElement(sib, localName);
241     }   
242     return static_cast<DOMElement*>(sib);
243 }
244
245 DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
246 {
247     DOMElement* e = getNextSiblingElement(n, localName);
248     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
249         e = getNextSiblingElement(e, localName);
250     return e;
251 }
252
253 DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
254 {
255     DOMElement* e = getPreviousSiblingElement(n, localName);
256     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
257         e = getPreviousSiblingElement(e, localName);
258     return e;
259 }
260
261 void XMLHelper::serialize(const DOMNode* n, std::string& buf, bool pretty)
262 {
263     static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };
264     static const XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDash, chDigit_8, chNull };
265
266     MemBufFormatTarget target;
267     DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);
268
269 #ifdef XMLTOOLING_XERCESC_COMPLIANT_DOMLS
270     DOMLSSerializer* serializer = static_cast<DOMImplementationLS*>(impl)->createLSSerializer();
271     XercesJanitor<DOMLSSerializer> janitor(serializer);
272     if (pretty && serializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
273         serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
274     DOMLSOutput *theOutput = static_cast<DOMImplementationLS*>(impl)->createLSOutput();
275     XercesJanitor<DOMLSOutput> j_theOutput(theOutput);
276     theOutput->setEncoding(UTF8);
277     theOutput->setByteStream(&target);
278     if (!serializer->write(n, theOutput))
279         throw XMLParserException("unable to serialize XML");
280 #else
281     DOMWriter* serializer = static_cast<DOMImplementationLS*>(impl)->createDOMWriter();
282     XercesJanitor<DOMWriter> janitor(serializer);
283     serializer->setEncoding(UTF8);
284     if (pretty && serializer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
285         serializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
286     if (!serializer->writeNode(&target, *n))
287         throw XMLParserException("unable to serialize XML");
288 #endif
289
290     buf.erase();
291     buf.append(reinterpret_cast<const char*>(target.getRawBuffer()),target.getLen());
292 }
293
294 namespace {
295     class StreamFormatTarget : public XMLFormatTarget
296     {
297     public:
298         StreamFormatTarget(std::ostream& out) : m_out(out) {}
299         ~StreamFormatTarget() {}
300
301         void writeChars(const XMLByte *const toWrite, const xsecsize_t count, XMLFormatter *const formatter) {
302             m_out.write(reinterpret_cast<const char*>(toWrite),count);
303         }
304
305         void flush() {
306             m_out.flush();
307         }
308
309     private:
310         std::ostream& m_out;
311     };
312 };
313
314 ostream& XMLHelper::serialize(const DOMNode* n, ostream& out, bool pretty)
315 {
316     static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };
317     static const XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDash, chDigit_8, chNull };
318
319     StreamFormatTarget target(out);
320     DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);
321
322 #ifdef XMLTOOLING_XERCESC_COMPLIANT_DOMLS
323     DOMLSSerializer* serializer = static_cast<DOMImplementationLS*>(impl)->createLSSerializer();
324     XercesJanitor<DOMLSSerializer> janitor(serializer);
325     if (pretty && serializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
326         serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
327     DOMLSOutput *theOutput = static_cast<DOMImplementationLS*>(impl)->createLSOutput();
328     XercesJanitor<DOMLSOutput> j_theOutput(theOutput);
329     theOutput->setEncoding(UTF8);
330     theOutput->setByteStream(&target);
331     if (!serializer->write(n, theOutput))
332         throw XMLParserException("unable to serialize XML");
333 #else
334     DOMWriter* serializer=(static_cast<DOMImplementationLS*>(impl))->createDOMWriter();
335     XercesJanitor<DOMWriter> janitor(serializer);
336     serializer->setEncoding(UTF8);
337     if (pretty && serializer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
338         serializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
339     if (!serializer->writeNode(&target,*n))
340         throw XMLParserException("unable to serialize XML");
341 #endif
342
343     return out;
344 }
345
346 ostream& xmltooling::operator<<(ostream& ostr, const DOMNode& node)
347 {
348     return XMLHelper::serialize(&node, ostr);
349 }
350
351 ostream& xmltooling::operator<<(ostream& ostr, const XMLObject& obj)
352 {
353     return ostr << *(obj.marshall());
354 }