Xerces 3 revisions.
[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     if (!attribute)
136         return NULL;
137     
138     int i;
139     const XMLCh* attributeValue=attribute->getTextContent();
140     if (attributeValue && (i=XMLString::indexOf(attributeValue,chColon))>0) {
141         XMLCh* prefix=new XMLCh[i+1];
142         XMLString::subString(prefix,attributeValue,0,i);
143         prefix[i]=chNull;
144         xmltooling::QName* ret=new xmltooling::QName(attribute->lookupNamespaceURI(prefix), attributeValue + i + 1, prefix);
145         delete[] prefix;
146         return ret;
147     }
148     
149     return new xmltooling::QName(attribute->lookupNamespaceURI(NULL), attributeValue);
150 }
151
152 DOMElement* XMLHelper::appendChildElement(DOMElement* parentElement, DOMElement* childElement)
153 {
154     DOMDocument* parentDocument = parentElement->getOwnerDocument();
155     if (childElement->getOwnerDocument() != parentDocument) {
156         childElement = static_cast<DOMElement*>(parentDocument->importNode(childElement, true));
157     }
158
159     parentElement->appendChild(childElement);
160     return childElement;
161 }
162
163 const XMLCh* XMLHelper::getTextContent(const DOMElement* e)
164 {
165     DOMNode* child=e->getFirstChild();
166     while (child) {
167         if (child->getNodeType()==DOMNode::TEXT_NODE)
168             return child->getNodeValue();
169         child=child->getNextSibling();
170     }
171     return NULL;
172 }
173
174 DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* localName)
175 {
176     DOMNode* child = n->getFirstChild();
177     while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)
178         child = child->getNextSibling();
179     if (child && localName) {
180         if (!XMLString::equals(localName,child->getLocalName()))
181             return getNextSiblingElement(child, localName);
182     }
183     return static_cast<DOMElement*>(child);
184 }    
185
186 DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* localName)
187 {
188     DOMNode* child = n->getLastChild();
189     while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)
190         child = child->getPreviousSibling();
191     if (child && localName) {
192         if (!XMLString::equals(localName,child->getLocalName()))
193             return getPreviousSiblingElement(child, localName);
194     }
195     return static_cast<DOMElement*>(child);
196 }    
197
198 DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
199 {
200     DOMElement* e = getFirstChildElement(n, localName);
201     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
202         e = getNextSiblingElement(e, localName);
203     return e;
204 }
205
206 DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
207 {
208     DOMElement* e = getLastChildElement(n, localName);
209     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
210         e = getPreviousSiblingElement(e, localName);
211     return e;
212 }
213
214 DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* localName)
215 {
216     DOMNode* sib = n->getNextSibling();
217     while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)
218         sib = sib->getNextSibling();
219     if (sib && localName) {
220         if (!XMLString::equals(localName,sib->getLocalName()))
221             return getNextSiblingElement(sib, localName);
222     }   
223     return static_cast<DOMElement*>(sib);
224 }
225
226 DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* localName)
227 {
228     DOMNode* sib = n->getPreviousSibling();
229     while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)
230         sib = sib->getPreviousSibling();
231     if (sib && localName) {
232         if (!XMLString::equals(localName,sib->getLocalName()))
233             return getPreviousSiblingElement(sib, localName);
234     }   
235     return static_cast<DOMElement*>(sib);
236 }
237
238 DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
239 {
240     DOMElement* e = getNextSiblingElement(n, localName);
241     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
242         e = getNextSiblingElement(e, localName);
243     return e;
244 }
245
246 DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
247 {
248     DOMElement* e = getPreviousSiblingElement(n, localName);
249     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
250         e = getPreviousSiblingElement(e, localName);
251     return e;
252 }
253
254 void XMLHelper::serialize(const DOMNode* n, std::string& buf, bool pretty)
255 {
256     static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };
257     static const XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDigit_8, chNull };
258
259     MemBufFormatTarget target;
260     DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);
261
262 #ifdef XMLTOOLING_XERCESC_COMPLIANT_DOMLS
263     DOMLSSerializer* serializer = static_cast<DOMImplementationLS*>(impl)->createLSSerializer();
264     XercesJanitor<DOMLSSerializer> janitor(serializer);
265     if (pretty && serializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
266         serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
267     DOMLSOutput *theOutput = static_cast<DOMImplementationLS*>(impl)->createLSOutput();\r
268     XercesJanitor<DOMLSOutput> j_theOutput(theOutput);\r
269     theOutput->setEncoding(UTF8);\r
270     theOutput->setByteStream(&target);
271     if (!serializer->write(n, theOutput))
272         throw XMLParserException("unable to serialize XML");
273 #else
274     DOMWriter* serializer = static_cast<DOMImplementationLS*>(impl)->createDOMWriter();
275     XercesJanitor<DOMWriter> janitor(serializer);
276     serializer->setEncoding(UTF8);
277     if (pretty && serializer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
278         serializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
279     if (!serializer->writeNode(&target, *n))
280         throw XMLParserException("unable to serialize XML");
281 #endif
282
283     buf.erase();
284     buf.append(reinterpret_cast<const char*>(target.getRawBuffer()),target.getLen());
285 }
286
287 namespace {
288     class StreamFormatTarget : public XMLFormatTarget
289     {
290     public:
291         StreamFormatTarget(std::ostream& out) : m_out(out) {}
292         ~StreamFormatTarget() {}
293
294         void writeChars(const XMLByte *const toWrite, const xsecsize_t count, XMLFormatter *const formatter) {
295             m_out.write(reinterpret_cast<const char*>(toWrite),count);
296         }
297
298         void flush() {
299             m_out.flush();
300         }
301
302     private:
303         std::ostream& m_out;
304     };
305 };
306
307 ostream& XMLHelper::serialize(const DOMNode* n, ostream& out, bool pretty)
308 {
309     static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };
310     static const XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDigit_8, chNull };
311
312     StreamFormatTarget target(out);
313     DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);
314
315 #ifdef XMLTOOLING_XERCESC_COMPLIANT_DOMLS
316     DOMLSSerializer* serializer = static_cast<DOMImplementationLS*>(impl)->createLSSerializer();
317     XercesJanitor<DOMLSSerializer> janitor(serializer);
318     if (pretty && serializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
319         serializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
320     DOMLSOutput *theOutput = static_cast<DOMImplementationLS*>(impl)->createLSOutput();\r
321     XercesJanitor<DOMLSOutput> j_theOutput(theOutput);\r
322     theOutput->setEncoding(UTF8);\r
323     theOutput->setByteStream(&target);
324     if (!serializer->write(n, theOutput))
325         throw XMLParserException("unable to serialize XML");
326 #else
327     DOMWriter* serializer=(static_cast<DOMImplementationLS*>(impl))->createDOMWriter();
328     XercesJanitor<DOMWriter> janitor(serializer);
329     serializer->setEncoding(UTF8);
330     if (pretty && serializer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty))
331         serializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, pretty);
332     if (!serializer->writeNode(&target,*n))
333         throw XMLParserException("unable to serialize XML");
334 #endif
335
336     return out;
337 }
338
339 ostream& xmltooling::operator<<(ostream& ostr, const DOMNode& node)
340 {
341     return XMLHelper::serialize(&node, ostr);
342 }
343
344 ostream& xmltooling::operator<<(ostream& ostr, const XMLObject& obj)
345 {
346     return ostr << *(obj.marshall());
347 }