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 bool XMLHelper::isNodeNamed(const xercesc::DOMNode* n, const XMLCh* ns, const XMLCh* local)
171 {
172     return (n && XMLString::equals(local,n->getLocalName()) && XMLString::equals(ns,n->getNamespaceURI()));
173 }
174
175 const XMLCh* XMLHelper::getTextContent(const DOMElement* e)
176 {
177     DOMNode* child=e->getFirstChild();
178     while (child) {
179         if (child->getNodeType()==DOMNode::TEXT_NODE)
180             return child->getNodeValue();
181         child=child->getNextSibling();
182     }
183     return NULL;
184 }
185
186 DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* localName)
187 {
188     DOMNode* child = n->getFirstChild();
189     while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)
190         child = child->getNextSibling();
191     if (child && localName) {
192         if (!XMLString::equals(localName,child->getLocalName()))
193             return getNextSiblingElement(child, localName);
194     }
195     return static_cast<DOMElement*>(child);
196 }    
197
198 DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* localName)
199 {
200     DOMNode* child = n->getLastChild();
201     while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)
202         child = child->getPreviousSibling();
203     if (child && localName) {
204         if (!XMLString::equals(localName,child->getLocalName()))
205             return getPreviousSiblingElement(child, localName);
206     }
207     return static_cast<DOMElement*>(child);
208 }    
209
210 DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
211 {
212     DOMElement* e = getFirstChildElement(n, localName);
213     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
214         e = getNextSiblingElement(e, localName);
215     return e;
216 }
217
218 DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
219 {
220     DOMElement* e = getLastChildElement(n, localName);
221     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
222         e = getPreviousSiblingElement(e, localName);
223     return e;
224 }
225
226 DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* localName)
227 {
228     DOMNode* sib = n->getNextSibling();
229     while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)
230         sib = sib->getNextSibling();
231     if (sib && localName) {
232         if (!XMLString::equals(localName,sib->getLocalName()))
233             return getNextSiblingElement(sib, localName);
234     }   
235     return static_cast<DOMElement*>(sib);
236 }
237
238 DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* localName)
239 {
240     DOMNode* sib = n->getPreviousSibling();
241     while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)
242         sib = sib->getPreviousSibling();
243     if (sib && localName) {
244         if (!XMLString::equals(localName,sib->getLocalName()))
245             return getPreviousSiblingElement(sib, localName);
246     }   
247     return static_cast<DOMElement*>(sib);
248 }
249
250 DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
251 {
252     DOMElement* e = getNextSiblingElement(n, localName);
253     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
254         e = getNextSiblingElement(e, localName);
255     return e;
256 }
257
258 DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
259 {
260     DOMElement* e = getPreviousSiblingElement(n, localName);
261     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
262         e = getPreviousSiblingElement(e, localName);
263     return e;
264 }
265
266 void XMLHelper::serialize(const DOMNode* n, std::string& buf, bool pretty)
267 {
268     static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };
269     static const XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDash, chDigit_8, chNull };
270
271     MemBufFormatTarget target;
272     DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);
273
274 #ifdef XMLTOOLING_XERCESC_COMPLIANT_DOMLS
275     DOMLSSerializer* serializer = static_cast<DOMImplementationLS*>(impl)->createLSSerializer();
276     XercesJanitor<DOMLSSerializer> janitor(serializer);
277     if (pretty && serializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
278         serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
279     DOMLSOutput *theOutput = static_cast<DOMImplementationLS*>(impl)->createLSOutput();
280     XercesJanitor<DOMLSOutput> j_theOutput(theOutput);
281     theOutput->setEncoding(UTF8);
282     theOutput->setByteStream(&target);
283     if (!serializer->write(n, theOutput))
284         throw XMLParserException("unable to serialize XML");
285 #else
286     DOMWriter* serializer = static_cast<DOMImplementationLS*>(impl)->createDOMWriter();
287     XercesJanitor<DOMWriter> janitor(serializer);
288     serializer->setEncoding(UTF8);
289     if (pretty && serializer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
290         serializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
291     if (!serializer->writeNode(&target, *n))
292         throw XMLParserException("unable to serialize XML");
293 #endif
294
295     buf.erase();
296     buf.append(reinterpret_cast<const char*>(target.getRawBuffer()),target.getLen());
297 }
298
299 namespace {
300     class StreamFormatTarget : public XMLFormatTarget
301     {
302     public:
303         StreamFormatTarget(std::ostream& out) : m_out(out) {}
304         ~StreamFormatTarget() {}
305
306         void writeChars(const XMLByte *const toWrite, const xsecsize_t count, XMLFormatter *const formatter) {
307             m_out.write(reinterpret_cast<const char*>(toWrite),count);
308         }
309
310         void flush() {
311             m_out.flush();
312         }
313
314     private:
315         std::ostream& m_out;
316     };
317 };
318
319 ostream& XMLHelper::serialize(const DOMNode* n, ostream& out, bool pretty)
320 {
321     static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };
322     static const XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDash, chDigit_8, chNull };
323
324     StreamFormatTarget target(out);
325     DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);
326
327 #ifdef XMLTOOLING_XERCESC_COMPLIANT_DOMLS
328     DOMLSSerializer* serializer = static_cast<DOMImplementationLS*>(impl)->createLSSerializer();
329     XercesJanitor<DOMLSSerializer> janitor(serializer);
330     if (pretty && serializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
331         serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
332     DOMLSOutput *theOutput = static_cast<DOMImplementationLS*>(impl)->createLSOutput();
333     XercesJanitor<DOMLSOutput> j_theOutput(theOutput);
334     theOutput->setEncoding(UTF8);
335     theOutput->setByteStream(&target);
336     if (!serializer->write(n, theOutput))
337         throw XMLParserException("unable to serialize XML");
338 #else
339     DOMWriter* serializer=(static_cast<DOMImplementationLS*>(impl))->createDOMWriter();
340     XercesJanitor<DOMWriter> janitor(serializer);
341     serializer->setEncoding(UTF8);
342     if (pretty && serializer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
343         serializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
344     if (!serializer->writeNode(&target,*n))
345         throw XMLParserException("unable to serialize XML");
346 #endif
347
348     return out;
349 }
350
351 ostream& xmltooling::operator<<(ostream& ostr, const DOMNode& node)
352 {
353     return XMLHelper::serialize(&node, ostr);
354 }
355
356 ostream& xmltooling::operator<<(ostream& ostr, const XMLObject& obj)
357 {
358     return ostr << *(obj.marshall());
359 }