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