Update copyright.
[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 std::ostream;
33 using std::list;
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 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                 QName* ret=new QName(e->lookupNamespaceURI(prefix), attributeValue + i + 1, prefix);
60                 delete[] prefix;
61                 return ret;
62             }
63             else {
64                 return new QName(e->lookupNamespaceURI(&chNull), 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 QName* XMLHelper::getNodeQName(const DOMNode* domNode)
109 {
110     if (domNode)
111         return new QName(domNode->getNamespaceURI(), domNode->getLocalName(), domNode->getPrefix());
112     return NULL; 
113 }
114
115 QName* XMLHelper::getAttributeValueAsQName(const DOMAttr* attribute)
116 {
117     if (!attribute)
118         return NULL;
119     
120     int i;
121     const XMLCh* attributeValue=attribute->getTextContent();
122     if (attributeValue && (i=XMLString::indexOf(attributeValue,chColon))>0) {
123         XMLCh* prefix=new XMLCh[i+1];
124         XMLString::subString(prefix,attributeValue,0,i);
125         prefix[i]=chNull;
126         QName* ret=new QName(attribute->lookupNamespaceURI(prefix), attributeValue + i + 1, prefix);
127         delete[] prefix;
128         return ret;
129     }
130     
131     return new QName(attribute->lookupNamespaceURI(NULL), attributeValue);
132 }
133
134 DOMElement* XMLHelper::appendChildElement(DOMElement* parentElement, DOMElement* childElement)
135 {
136     DOMDocument* parentDocument = parentElement->getOwnerDocument();
137     if (childElement->getOwnerDocument() != parentDocument) {
138         childElement = static_cast<DOMElement*>(parentDocument->importNode(childElement, true));
139     }
140
141     parentElement->appendChild(childElement);
142     return childElement;
143 }
144
145 const XMLCh* XMLHelper::getTextContent(const DOMElement* e)
146 {
147     DOMNode* child=e->getFirstChild();
148     while (child) {
149         if (child->getNodeType()==DOMNode::TEXT_NODE)
150             return child->getNodeValue();
151         child=child->getNextSibling();
152     }
153     return NULL;
154 }
155
156 DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* localName)
157 {
158     DOMNode* child = n->getFirstChild();
159     while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)
160         child = child->getNextSibling();
161     if (child && localName) {
162         if (!XMLString::equals(localName,child->getLocalName()))
163             return getNextSiblingElement(child, localName);
164     }
165     return static_cast<DOMElement*>(child);
166 }    
167
168 DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* localName)
169 {
170     DOMNode* child = n->getLastChild();
171     while (child && child->getNodeType() != DOMNode::ELEMENT_NODE)
172         child = child->getPreviousSibling();
173     if (child && localName) {
174         if (!XMLString::equals(localName,child->getLocalName()))
175             return getPreviousSiblingElement(child, localName);
176     }
177     return static_cast<DOMElement*>(child);
178 }    
179
180 DOMElement* XMLHelper::getFirstChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
181 {
182     DOMElement* e = getFirstChildElement(n, localName);
183     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
184         e = getNextSiblingElement(e, localName);
185     return e;
186 }
187
188 DOMElement* XMLHelper::getLastChildElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
189 {
190     DOMElement* e = getLastChildElement(n, localName);
191     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
192         e = getPreviousSiblingElement(e, localName);
193     return e;
194 }
195
196 DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* localName)
197 {
198     DOMNode* sib = n->getNextSibling();
199     while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)
200         sib = sib->getNextSibling();
201     if (sib && localName) {
202         if (!XMLString::equals(localName,sib->getLocalName()))
203             return getNextSiblingElement(sib, localName);
204     }   
205     return static_cast<DOMElement*>(sib);
206 }
207
208 DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* localName)
209 {
210     DOMNode* sib = n->getPreviousSibling();
211     while (sib && sib->getNodeType() != DOMNode::ELEMENT_NODE)
212         sib = sib->getPreviousSibling();
213     if (sib && localName) {
214         if (!XMLString::equals(localName,sib->getLocalName()))
215             return getPreviousSiblingElement(sib, localName);
216     }   
217     return static_cast<DOMElement*>(sib);
218 }
219
220 DOMElement* XMLHelper::getNextSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
221 {
222     DOMElement* e = getNextSiblingElement(n, localName);
223     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
224         e = getNextSiblingElement(e, localName);
225     return e;
226 }
227
228 DOMElement* XMLHelper::getPreviousSiblingElement(const DOMNode* n, const XMLCh* ns, const XMLCh* localName)
229 {
230     DOMElement* e = getPreviousSiblingElement(n, localName);
231     while (e && !XMLString::equals(e->getNamespaceURI(),ns))
232         e = getPreviousSiblingElement(e, localName);
233     return e;
234 }
235
236 void XMLHelper::serialize(const DOMNode* n, std::string& buf)
237 {
238     static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };
239     static const XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDigit_8, chNull };
240     DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);
241     DOMWriter* serializer=(static_cast<DOMImplementationLS*>(impl))->createDOMWriter();
242     XercesJanitor<DOMWriter> janitor(serializer);
243     serializer->setEncoding(UTF8);
244     MemBufFormatTarget target;
245     if (!serializer->writeNode(&target,*n))
246         throw XMLParserException("unable to serialize XML");
247     buf.erase();
248     buf.append(reinterpret_cast<const char*>(target.getRawBuffer()),target.getLen());
249 }
250
251 namespace {
252     class StreamFormatTarget : public XMLFormatTarget
253     {
254     public:
255         StreamFormatTarget(std::ostream& out) : m_out(out) {}
256         ~StreamFormatTarget() {}
257
258         void writeChars(const XMLByte *const toWrite, const unsigned int count, XMLFormatter *const formatter) {
259             m_out.write(reinterpret_cast<const char*>(toWrite),count);
260         }
261
262         void flush() {
263             m_out.flush();
264         }
265
266     private:
267         std::ostream& m_out;
268     };
269 };
270
271 ostream& XMLHelper::serialize(const DOMNode* n, ostream& out)
272 {
273     static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };
274     static const XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDigit_8, chNull };
275     DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);
276     DOMWriter* serializer=(static_cast<DOMImplementationLS*>(impl))->createDOMWriter();
277     XercesJanitor<DOMWriter> janitor(serializer);
278     serializer->setEncoding(UTF8);
279     StreamFormatTarget target(out);
280     if (!serializer->writeNode(&target,*n))
281         throw XMLParserException("unable to serialize XML");
282     return out;
283 }
284
285 ostream& xmltooling::operator<<(ostream& ostr, const DOMNode& node)
286 {
287     return XMLHelper::serialize(&node, ostr);
288 }
289
290 ostream& xmltooling::operator<<(ostream& ostr, const XMLObject& obj)
291 {
292     return ostr << *(obj.marshall());
293 }