2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
24 * C++ DDF abstraction for interpretive RPC
28 #include "remoting/ddf.h"
31 #include <boost/lexical_cast.hpp>
32 #include <xercesc/dom/DOM.hpp>
33 #include <xercesc/util/XMLUniDefs.hpp>
34 #include <xmltooling/XMLToolingConfig.h>
35 #include <xmltooling/util/ParserPool.h>
36 #include <xmltooling/util/URLEncoder.h>
37 #include <xmltooling/util/XMLHelper.h>
39 using namespace shibsp;
40 using namespace xmltooling;
41 using namespace xercesc;
44 // defensive string functions
46 size_t ddf_strlen(const char* s)
48 return s ? strlen(s) : 0;
51 char* ddf_strdup(const char* s)
53 return (s && *s) ? strdup(s) : nullptr;
56 #define MAX_NAME_LEN 255
58 /* Parses '.' notation paths, where each component is at most MAX_NAME_LEN long.
59 path contains the address of a constant string which is the current path.
60 name points to a buffer in which to place the first path component.
61 After execution, the path pointer will be moved past the first dot.
62 The actual path string is never modified. Only name is written to.
63 The name buffer is returned from the function. */
64 char* ddf_token(const char** path, char* name)
67 if (*path==nullptr || **path==0)
70 const char* temp=strchr(*path,'.');
72 strncpy(name,*path,MAX_NAME_LEN);
76 else if (temp>*path) {
77 strncpy(name,*path,temp-*path);
86 // body implementation
88 struct shibsp::ddf_body_t {
89 ddf_body_t() : name(nullptr), parent(nullptr), next(nullptr), prev(nullptr), type(DDF_EMPTY) {}
91 char* name; // name of node
92 ddf_body_t* parent; // parent node, if any
93 ddf_body_t* next; // next node, if any
94 ddf_body_t* prev; // previous node, if any
105 } type; // data type of node
118 } value; // value of node
121 // library implementation
123 DDF::DDF() : m_handle(nullptr)
127 DDF::DDF(const char* n)
129 m_handle=new(nothrow) ddf_body_t;
133 DDF::DDF(const char* n, const char* val, bool safe)
135 m_handle=new(nothrow) ddf_body_t;
137 string(const_cast<char*>(val), true, safe);
140 DDF::DDF(const char* n, long val)
142 m_handle=new(nothrow) ddf_body_t;
147 DDF::DDF(const char* n, double val)
149 m_handle=new(nothrow) ddf_body_t;
154 DDF::DDF(const char* n, void* val)
156 m_handle=new(nothrow) ddf_body_t;
163 remove().empty().name(nullptr);
169 DDF DDF::copy() const
171 if (m_handle==nullptr)
174 switch (m_handle->type) {
175 case ddf_body_t::DDF_EMPTY:
176 return DDF(m_handle->name);
177 case ddf_body_t::DDF_STRING:
178 case ddf_body_t::DDF_STRING_UNSAFE:
179 return DDF(m_handle->name,m_handle->value.string,(m_handle->type==ddf_body_t::DDF_STRING));
180 case ddf_body_t::DDF_INT:
181 return DDF(m_handle->name,m_handle->value.integer);
182 case ddf_body_t::DDF_FLOAT:
183 return DDF(m_handle->name,m_handle->value.floating);
184 case ddf_body_t::DDF_POINTER:
185 return DDF(m_handle->name,m_handle->value.pointer);
186 case ddf_body_t::DDF_STRUCT:
187 case ddf_body_t::DDF_LIST:
189 DDF copy(m_handle->name), temp;
190 if (m_handle->type==ddf_body_t::DDF_STRUCT)
194 ddf_body_t* child=m_handle->value.children.first;
197 DDF temp2=temp.copy();
199 if (copy.m_handle==nullptr)
201 if (m_handle->value.children.current==child)
202 copy.m_handle->value.children.current=copy.m_handle->value.children.last;
211 const char* DDF::name() const
213 return (m_handle) ? m_handle->name : nullptr;
216 DDF& DDF::name(const char* name)
218 char trunc_name[MAX_NAME_LEN+1]="";
222 free(m_handle->name);
224 strncpy(trunc_name,name,MAX_NAME_LEN);
225 trunc_name[MAX_NAME_LEN]='\0';
226 m_handle->name=ddf_strdup(trunc_name);
231 m_handle->name=nullptr;
236 bool DDF::isnull() const
238 return m_handle ? false : true;
241 bool DDF::isempty() const
243 return m_handle ? (m_handle->type==ddf_body_t::DDF_EMPTY) : false;
246 bool DDF::isstring() const
248 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING || m_handle->type==ddf_body_t::DDF_STRING_UNSAFE) : false;
251 bool DDF::isint() const
253 return m_handle ? (m_handle->type==ddf_body_t::DDF_INT) : false;
256 bool DDF::isfloat() const
258 return m_handle ? (m_handle->type==ddf_body_t::DDF_FLOAT) : false;
261 bool DDF::isstruct() const
263 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRUCT) : false;
266 bool DDF::islist() const
268 return m_handle ? (m_handle->type==ddf_body_t::DDF_LIST) : false;
271 bool DDF::ispointer() const
273 return m_handle ? (m_handle->type==ddf_body_t::DDF_POINTER) : false;
276 const char* DDF::string() const
278 return isstring() ? m_handle->value.string : nullptr;
281 long DDF::integer() const
284 switch(m_handle->type) {
285 case ddf_body_t::DDF_INT:
286 return m_handle->value.integer;
287 case ddf_body_t::DDF_FLOAT:
288 return static_cast<long>(m_handle->value.floating);
289 case ddf_body_t::DDF_STRING:
290 case ddf_body_t::DDF_STRING_UNSAFE:
291 return m_handle->value.string ? atol(m_handle->value.string) : 0;
292 case ddf_body_t::DDF_STRUCT:
293 case ddf_body_t::DDF_LIST:
294 return m_handle->value.children.count;
300 double DDF::floating() const
303 switch(m_handle->type) {
304 case ddf_body_t::DDF_INT:
305 return m_handle->value.integer;
306 case ddf_body_t::DDF_FLOAT:
307 return m_handle->value.floating;
308 case ddf_body_t::DDF_STRING:
309 case ddf_body_t::DDF_STRING_UNSAFE:
310 return m_handle->value.string ? atof(m_handle->value.string) : 0;
311 case ddf_body_t::DDF_STRUCT:
312 case ddf_body_t::DDF_LIST:
313 return m_handle->value.children.count;
319 void* DDF::pointer() const
321 return ispointer() ? m_handle->value.pointer : nullptr;
324 size_t DDF::strlen() const
326 return ddf_strlen(string());
329 bool DDF::operator==(const char* s) const
331 if (string()==nullptr || s==nullptr)
332 return (string()==nullptr && s==nullptr);
334 return (::strcmp(string(),s)==0);
340 switch (m_handle->type) {
341 case ddf_body_t::DDF_STRING:
342 case ddf_body_t::DDF_STRING_UNSAFE:
343 if (m_handle->value.string)
344 free(m_handle->value.string);
346 case ddf_body_t::DDF_LIST:
347 case ddf_body_t::DDF_STRUCT:
350 while (m_handle->value.children.first)
352 temp.m_handle=m_handle->value.children.first;
357 m_handle->type=ddf_body_t::DDF_EMPTY;
362 DDF& DDF::string(char* val, bool copyit, bool safe)
364 if (empty().m_handle) {
365 m_handle->value.string = copyit ? ddf_strdup(val) : val;
366 if (!m_handle->value.string && val && *val)
368 m_handle->type=(safe ? ddf_body_t::DDF_STRING : ddf_body_t::DDF_STRING_UNSAFE);
373 DDF& DDF::string(const char* val)
375 return string(const_cast<char*>(val), true);
378 DDF& DDF::unsafe_string(const char* val)
380 return string(const_cast<char*>(val), true, false);
383 DDF& DDF::string(long val)
385 std::string buf = boost::lexical_cast<std::string>(val);
386 return string(buf.c_str());
389 DDF& DDF::string(double val)
391 std::string buf = boost::lexical_cast<std::string>(val);
392 return string(buf.c_str());
395 DDF& DDF::integer(long val)
397 if (empty().m_handle) {
398 m_handle->value.integer=val;
399 m_handle->type=ddf_body_t::DDF_INT;
404 DDF& DDF::integer(const char* val)
406 if (empty().m_handle) {
407 m_handle->value.integer=(val ? atol(val) : 0);
408 m_handle->type=ddf_body_t::DDF_INT;
413 DDF& DDF::floating(double val)
415 if (empty().m_handle) {
416 m_handle->value.floating=val;
417 m_handle->type=ddf_body_t::DDF_FLOAT;
422 DDF& DDF::floating(const char* val)
424 if (empty().m_handle) {
425 m_handle->value.floating=(val ? atof(val) : 0);
426 m_handle->type=ddf_body_t::DDF_FLOAT;
431 DDF& DDF::structure()
433 if (empty().m_handle) {
434 m_handle->type=ddf_body_t::DDF_STRUCT;
435 m_handle->value.children.first=nullptr;
436 m_handle->value.children.last=nullptr;
437 m_handle->value.children.current=nullptr;
438 m_handle->value.children.count=0;
445 if (empty().m_handle) {
446 m_handle->type=ddf_body_t::DDF_LIST;
447 m_handle->value.children.first=nullptr;
448 m_handle->value.children.last=nullptr;
449 m_handle->value.children.current=nullptr;
450 m_handle->value.children.count=0;
455 DDF& DDF::pointer(void* val)
457 if (empty().m_handle) {
458 m_handle->value.pointer=val;
459 m_handle->type=ddf_body_t::DDF_POINTER;
464 DDF& DDF::add(DDF& child)
466 if ((!isstruct() && !islist()) || !child.m_handle)
469 if (m_handle==child.m_handle->parent)
475 getmember(child.name()).destroy();
479 if (!m_handle->value.children.first)
480 m_handle->value.children.first=child.m_handle;
482 m_handle->value.children.last->next=child.m_handle;
483 child.m_handle->prev=m_handle->value.children.last;
485 m_handle->value.children.last=child.m_handle;
486 child.m_handle->parent=m_handle;
487 m_handle->value.children.count++;
491 DDF& DDF::addbefore(DDF& child, DDF& before)
493 if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
497 if (m_handle->value.children.first==before.m_handle)
498 m_handle->value.children.first=child.m_handle;
499 child.m_handle->prev=before.m_handle->prev;
500 if (child.m_handle->prev)
501 child.m_handle->prev->next=child.m_handle;
502 before.m_handle->prev=child.m_handle;
503 child.m_handle->next=before.m_handle;
504 child.m_handle->parent=m_handle;
505 m_handle->value.children.count++;
509 DDF& DDF::addafter(DDF& child, DDF& after)
511 if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
515 if (m_handle->value.children.last==after.m_handle)
516 m_handle->value.children.last=child.m_handle;
517 child.m_handle->next=after.m_handle->next;
518 if (child.m_handle->next)
519 child.m_handle->next->prev=child.m_handle;
520 after.m_handle->next=child.m_handle;
521 child.m_handle->prev=after.m_handle;
522 child.m_handle->parent=m_handle;
523 m_handle->value.children.count++;
527 void DDF::swap(DDF& arg)
529 ddf_body_t* temp=arg.m_handle;
530 arg.m_handle=m_handle;
536 if (!m_handle || !m_handle->parent)
540 m_handle->next->prev=m_handle->prev;
543 m_handle->prev->next=m_handle->next;
545 if (m_handle->parent->value.children.first==m_handle)
546 m_handle->parent->value.children.first=m_handle->next;
548 if (m_handle->parent->value.children.last==m_handle)
549 m_handle->parent->value.children.last=m_handle->prev;
551 if (m_handle->parent->value.children.current==m_handle)
552 m_handle->parent->value.children.current=m_handle->prev;
554 m_handle->parent->value.children.count--;
555 m_handle->parent=nullptr;
556 m_handle->next=nullptr;
557 m_handle->prev=nullptr;
561 DDF DDF::parent() const
565 p.m_handle=(m_handle ? m_handle->parent : nullptr);
573 if (islist() || isstruct())
574 f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
582 if ((islist() || isstruct()) && m_handle->value.children.current!=m_handle->value.children.last) {
583 if (!m_handle->value.children.current)
584 n.m_handle=m_handle->value.children.current=m_handle->value.children.first;
586 n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
595 if ((islist() || isstruct()) && m_handle->value.children.last) {
596 m_handle->value.children.current=m_handle->value.children.last->prev;
597 l.m_handle=m_handle->value.children.last;
606 if (islist() || isstruct()) {
607 p.m_handle=m_handle->value.children.current;
609 m_handle->value.children.current=m_handle->value.children.current->prev;
614 DDF DDF::operator[](const char* path) const
616 return getmember(path);
619 DDF DDF::operator[](unsigned long index) const
623 if (islist() && index<m_handle->value.children.count) {
624 for (d.m_handle=m_handle->value.children.first; index; index--)
625 d.m_handle=d.m_handle->next;
628 throw range_error("DDF object not a list with >=index+1 elements");
632 DDF DDF::addmember(const char* path)
634 char name[MAX_NAME_LEN+1];
635 const char* path_ptr=path;
637 if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
641 DDF new_member=getmember(name);
642 if (!new_member.m_handle) {
644 new_member=add(temp);
647 if (new_member.m_handle) {
648 if (ddf_strlen(path_ptr)>0) {
649 DDF last_member=new_member.addmember(path_ptr);
650 if (!last_member.m_handle)
651 return new_member.destroy();
662 DDF DDF::getmember(const char* path) const
665 char name[MAX_NAME_LEN+1];
666 const char* path_ptr=path;
668 ddf_token(&path_ptr, name);
671 else if (*name == '[') {
672 unsigned long i = strtoul(name+1, nullptr, 10);
673 if (islist() && i < m_handle->value.children.count)
674 current=operator[](i);
678 else if (isstruct()) {
679 current.m_handle = m_handle->value.children.first;
680 while (current.m_handle && strcmp(current.m_handle->name,name) != 0)
681 current.m_handle = current.m_handle->next;
684 current.m_handle = m_handle->value.children.first;
685 return current.getmember(path);
688 if (current.m_handle && path_ptr && *path_ptr)
689 current = current.getmember(path_ptr);
694 void ddf_print_indent(FILE* f, int indent)
696 for (; indent>0; indent--)
700 void DDF::dump(FILE* f, int indent) const
705 ddf_print_indent(f,indent);
707 switch (m_handle->type) {
709 case ddf_body_t::DDF_EMPTY:
712 fprintf(f," %s",m_handle->name);
715 case ddf_body_t::DDF_STRING:
716 case ddf_body_t::DDF_STRING_UNSAFE:
718 fprintf(f,"char* %s = ",m_handle->name);
720 fprintf(f,"char* = ");
721 if (const char* chptr=m_handle->value.string) {
728 fprintf(f,"nullptr");
731 case ddf_body_t::DDF_INT:
733 fprintf(f,"long %s = ",m_handle->name);
735 fprintf(f,"long = ");
736 fprintf(f,"%ld",m_handle->value.integer);
739 case ddf_body_t::DDF_FLOAT:
741 fprintf(f,"double %s = ",m_handle->name);
743 fprintf(f,"double = ");
744 fprintf(f,"%.15f",m_handle->value.floating);
747 case ddf_body_t::DDF_STRUCT:
748 fprintf(f,"struct ");
750 fprintf(f,"%s ",m_handle->name);
752 if (m_handle->value.children.count) {
755 child.m_handle=m_handle->value.children.first;
756 while (child.m_handle) {
757 child.dump(f,indent+2);
758 child.m_handle=child.m_handle->next;
761 ddf_print_indent(f,indent);
765 case ddf_body_t::DDF_LIST:
768 fprintf(f," %s",m_handle->name);
769 fprintf(f,"[%lu] {",m_handle->value.children.count);
770 if (m_handle->value.children.count) {
773 child.m_handle=m_handle->value.children.first;
774 while (child.m_handle) {
775 child.dump(f,indent+2);
776 child.m_handle=child.m_handle->next;
779 ddf_print_indent(f,indent);
783 case ddf_body_t::DDF_POINTER:
785 fprintf(f,"void* %s = ",m_handle->name);
787 fprintf(f,"void* = ");
788 if (m_handle->value.pointer)
789 fprintf(f,"%p",m_handle->value.pointer);
791 fprintf(f,"nullptr");
795 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
799 fprintf(f,"nullptr");
803 // Serialization is fairly easy. We have to walk the DDF and hand-generate a
804 // wddxPacket XML fragment, with some simple extensions. We escape the four major
805 // special characters, which requires that we output strings one char at a time.
807 void xml_encode(ostream& os, const char* start)
810 while (start && *start) {
811 pos = strcspn(start, "\"<>&");
818 case '"': os << """; break;
819 case '<': os << "<"; break;
820 case '>': os << ">"; break;
821 case '&': os << "&"; break;
822 default: os << *start;
829 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
834 case ddf_body_t::DDF_STRING:
835 case ddf_body_t::DDF_STRING_UNSAFE:
837 if (name_attr && p->name) {
839 xml_encode(os,p->name);
842 if (p->value.string) {
843 if (p->type == ddf_body_t::DDF_STRING) {
845 xml_encode(os,p->value.string);
848 os << " unsafe=\"1\">";
849 xml_encode(os,XMLToolingConfig::getConfig().getURLEncoder()->encode(p->value.string).c_str());
857 case ddf_body_t::DDF_INT:
859 if (name_attr && p->name) {
861 xml_encode(os,p->name);
864 os << '>' << p->value.integer << "</number>";
867 case ddf_body_t::DDF_FLOAT:
869 if (name_attr && p->name) {
871 xml_encode(os,p->name);
874 os << '>' << fixed << p->value.floating << dec << "</number>";
877 case ddf_body_t::DDF_STRUCT:
880 if (name_attr && p->name) {
882 xml_encode(os,p->name);
886 ddf_body_t* child=p->value.children.first;
888 os << "<var name=\"";
889 xml_encode(os,child->name);
891 serialize(child,os,false);
899 case ddf_body_t::DDF_LIST:
901 os << "<array length=\"" << p->value.children.count << '"';
902 if (name_attr && p->name) {
904 xml_encode(os,p->name);
908 ddf_body_t* child=p->value.children.first;
917 case ddf_body_t::DDF_EMPTY:
918 case ddf_body_t::DDF_POINTER:
921 if (name_attr && p->name) {
923 xml_encode(os,p->name);
934 // The stream insertion will work for any ostream-based object.
936 SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
939 os << "<wddxPacket version=\"1.0\" lowercase=\"no\"><header/><data>";
940 serialize(obj.m_handle,os);
941 os << "</data></wddxPacket>";
945 // This is a DTD internal subset based on a compatible permutation of the WDDX spec, with the
946 // extension of a name attribute on all the typed elements, which DDF has, but WDDX does not.
949 static const char* g_DocType=
951 <!DOCTYPE wddxPacket [\n\
952 <!ELEMENT wddxPacket (header, data)>\n\
953 <!ATTLIST wddxPacket version CDATA #FIXED \"1.0\" lowercase (yes|no) \"yes\">\n\
954 <!ELEMENT header (comment?)>\n\
955 <!ELEMENT comment (#PCDATA)>\n\
956 <!ELEMENT data (null | number | string | array | struct)>\n\
957 <!ELEMENT null EMPTY>\n\
958 <!ATTLIST null name CDATA #IMPLIED type CDATA #IMPLIED>\n\
959 <!ELEMENT string (#PCDATA | char)*>\n\
960 <!ATTLIST string name CDATA #IMPLIED type CDATA #IMPLIED>\n\
961 <!ELEMENT char EMPTY>\n\
962 <!ATTLIST char code CDATA #REQUIRED>\n\
963 <!ELEMENT number (#PCDATA)>\n\
964 <!ATTLIST number name CDATA #IMPLIED type CDATA #IMPLIED>\n\
965 <!ELEMENT array (null | number | string | array | struct)*>\n\
966 <!ATTLIST array length CDATA #REQUIRED name CDATA #IMPLIED type CDATA #IMPLIED>\n\
967 <!ELEMENT struct (var*)>\n\
968 <!ATTLIST struct name CDATA #IMPLIED type CDATA #IMPLIED>\n\
969 <!ELEMENT var (null | number | string | array | struct)>\n\
970 <!ATTLIST var name CDATA #REQUIRED>\n\
974 // This function constructs a DDF object equivalent to the wddx data element rooted
977 static const XMLCh _no[] = UNICODE_LITERAL_2(n,o);
978 static const XMLCh _name[] = UNICODE_LITERAL_4(n,a,m,e);
979 static const XMLCh _var[] = UNICODE_LITERAL_3(v,a,r);
980 static const XMLCh _string[] = UNICODE_LITERAL_6(s,t,r,i,n,g);
981 static const XMLCh _number[] = UNICODE_LITERAL_6(n,u,m,b,e,r);
982 static const XMLCh _array[] = UNICODE_LITERAL_5(a,r,r,a,y);
983 static const XMLCh _struct[] = UNICODE_LITERAL_6(s,t,r,u,c,t);
984 static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e);
985 static const XMLCh _unsafe[] = UNICODE_LITERAL_6(u,n,s,a,f,e);
987 DDF deserialize(DOMElement* root, bool lowercase)
990 auto_ptr_char name_val(root->getAttributeNS(nullptr, _name));
991 if (name_val.get() && *name_val.get()) {
993 for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
994 obj.name(name_val.get());
997 const XMLCh* tag=root->getTagName();
998 if (XMLString::equals(tag,_var)) {
999 root=XMLHelper::getFirstChildElement(root);
1000 tag=(root ? root->getTagName() : &chNull);
1003 if (XMLString::equals(tag,_string)) {
1004 DOMNode* child=root->getFirstChild();
1005 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
1006 const XMLCh* unsafe = root->getAttributeNS(nullptr, _unsafe);
1007 if (unsafe && *unsafe==chDigit_1) {
1008 // If it's unsafe, it's not UTF-8 data, so we have to convert to ASCII and decode it.
1009 char* encoded = XMLString::transcode(child->getNodeValue());
1010 XMLToolingConfig::getConfig().getURLEncoder()->decode(encoded);
1011 obj.string(encoded, true, false); // re-copy into free-able buffer, plus mark unsafe
1012 XMLString::release(&encoded);
1015 char* val = toUTF8(child->getNodeValue(), true); // use malloc
1016 obj.string(val, false); // don't re-copy the string
1020 else if (XMLString::equals(tag,_number)) {
1021 DOMNode* child=root->getFirstChild();
1022 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
1023 auto_ptr_char val(child->getNodeValue());
1024 if (val.get() && strchr(val.get(),'.'))
1025 obj.floating(val.get());
1027 obj.integer(val.get());
1030 else if (XMLString::equals(tag,_array)) {
1032 DOMNodeList* children=root->getChildNodes();
1033 for (unsigned int i=0; children && i<children->getLength(); i++)
1034 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
1035 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
1039 else if (XMLString::equals(tag,_struct)) {
1041 DOMNodeList* children=root->getChildNodes();
1042 for (unsigned int i=0; children && i<children->getLength(); i++)
1043 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
1044 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
1052 SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
1054 // Parse the input stream into a DOM tree and construct the equivalent DDF.
1055 DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(is);
1056 XercesJanitor<DOMDocument> docj(doc);
1057 const XMLCh* lowercase=doc->getDocumentElement()->getAttribute(_lowercase);
1058 DOMElement* first=XMLHelper::getFirstChildElement(XMLHelper::getLastChildElement(doc->getDocumentElement()));
1060 obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);