2 * Copyright 2001-2010 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * C++ DDF abstraction for interpretive RPC
24 #include "remoting/ddf.h"
27 # define snprintf _snprintf
31 #include <xercesc/dom/DOM.hpp>
32 #include <xercesc/util/XMLUniDefs.hpp>
33 #include <xmltooling/XMLToolingConfig.h>
34 #include <xmltooling/util/ParserPool.h>
35 #include <xmltooling/util/URLEncoder.h>
36 #include <xmltooling/util/XMLHelper.h>
38 using namespace shibsp;
39 using namespace xmltooling;
40 using namespace xercesc;
43 // defensive string functions
45 size_t ddf_strlen(const char* s)
47 return s ? strlen(s) : 0;
50 char* ddf_strdup(const char* s)
52 return (s && *s) ? strdup(s) : nullptr;
55 #define MAX_NAME_LEN 255
57 /* Parses '.' notation paths, where each component is at most MAX_NAME_LEN long.
58 path contains the address of a constant string which is the current path.
59 name points to a buffer in which to place the first path component.
60 After execution, the path pointer will be moved past the first dot.
61 The actual path string is never modified. Only name is written to.
62 The name buffer is returned from the function. */
63 char* ddf_token(const char** path, char* name)
66 if (*path==nullptr || **path==0)
69 const char* temp=strchr(*path,'.');
71 strncpy(name,*path,MAX_NAME_LEN);
75 else if (temp>*path) {
76 strncpy(name,*path,temp-*path);
85 // body implementation
87 struct shibsp::ddf_body_t {
88 ddf_body_t() : name(nullptr), parent(nullptr), next(nullptr), prev(nullptr), type(DDF_EMPTY) {}
90 char* name; // name of node
91 ddf_body_t* parent; // parent node, if any
92 ddf_body_t* next; // next node, if any
93 ddf_body_t* prev; // previous node, if any
104 } type; // data type of node
117 } value; // value of node
120 // library implementation
122 DDF::DDF() : m_handle(nullptr)
126 DDF::DDF(const char* n)
128 m_handle=new(nothrow) ddf_body_t;
132 DDF::DDF(const char* n, const char* val, bool safe)
134 m_handle=new(nothrow) ddf_body_t;
136 string(const_cast<char*>(val), true, safe);
139 DDF::DDF(const char* n, long val)
141 m_handle=new(nothrow) ddf_body_t;
146 DDF::DDF(const char* n, double val)
148 m_handle=new(nothrow) ddf_body_t;
153 DDF::DDF(const char* n, void* val)
155 m_handle=new(nothrow) ddf_body_t;
162 remove().empty().name(nullptr);
168 DDF DDF::copy() const
170 if (m_handle==nullptr)
173 switch (m_handle->type) {
174 case ddf_body_t::DDF_EMPTY:
175 return DDF(m_handle->name);
176 case ddf_body_t::DDF_STRING:
177 case ddf_body_t::DDF_STRING_UNSAFE:
178 return DDF(m_handle->name,m_handle->value.string,(m_handle->type==ddf_body_t::DDF_STRING));
179 case ddf_body_t::DDF_INT:
180 return DDF(m_handle->name,m_handle->value.integer);
181 case ddf_body_t::DDF_FLOAT:
182 return DDF(m_handle->name,m_handle->value.floating);
183 case ddf_body_t::DDF_POINTER:
184 return DDF(m_handle->name,m_handle->value.pointer);
185 case ddf_body_t::DDF_STRUCT:
186 case ddf_body_t::DDF_LIST:
188 DDF copy(m_handle->name), temp;
189 if (m_handle->type==ddf_body_t::DDF_STRUCT)
193 ddf_body_t* child=m_handle->value.children.first;
196 DDF temp2=temp.copy();
198 if (copy.m_handle==nullptr)
200 if (m_handle->value.children.current==child)
201 copy.m_handle->value.children.current=copy.m_handle->value.children.last;
210 const char* DDF::name() const
212 return (m_handle) ? m_handle->name : nullptr;
215 DDF& DDF::name(const char* name)
217 char trunc_name[MAX_NAME_LEN+1]="";
221 free(m_handle->name);
223 strncpy(trunc_name,name,MAX_NAME_LEN);
224 trunc_name[MAX_NAME_LEN]='\0';
225 m_handle->name=ddf_strdup(trunc_name);
230 m_handle->name=nullptr;
235 bool DDF::isnull() const
237 return m_handle ? false : true;
240 bool DDF::isempty() const
242 return m_handle ? (m_handle->type==ddf_body_t::DDF_EMPTY) : false;
245 bool DDF::isstring() const
247 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING || m_handle->type==ddf_body_t::DDF_STRING_UNSAFE) : false;
250 bool DDF::isint() const
252 return m_handle ? (m_handle->type==ddf_body_t::DDF_INT) : false;
255 bool DDF::isfloat() const
257 return m_handle ? (m_handle->type==ddf_body_t::DDF_FLOAT) : false;
260 bool DDF::isstruct() const
262 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRUCT) : false;
265 bool DDF::islist() const
267 return m_handle ? (m_handle->type==ddf_body_t::DDF_LIST) : false;
270 bool DDF::ispointer() const
272 return m_handle ? (m_handle->type==ddf_body_t::DDF_POINTER) : false;
275 const char* DDF::string() const
277 return isstring() ? m_handle->value.string : nullptr;
280 long DDF::integer() const
283 switch(m_handle->type) {
284 case ddf_body_t::DDF_INT:
285 return m_handle->value.integer;
286 case ddf_body_t::DDF_FLOAT:
287 return static_cast<long>(m_handle->value.floating);
288 case ddf_body_t::DDF_STRING:
289 case ddf_body_t::DDF_STRING_UNSAFE:
290 return m_handle->value.string ? atol(m_handle->value.string) : 0;
291 case ddf_body_t::DDF_STRUCT:
292 case ddf_body_t::DDF_LIST:
293 return m_handle->value.children.count;
299 double DDF::floating() const
302 switch(m_handle->type) {
303 case ddf_body_t::DDF_INT:
304 return m_handle->value.integer;
305 case ddf_body_t::DDF_FLOAT:
306 return m_handle->value.floating;
307 case ddf_body_t::DDF_STRING:
308 case ddf_body_t::DDF_STRING_UNSAFE:
309 return m_handle->value.string ? atof(m_handle->value.string) : 0;
310 case ddf_body_t::DDF_STRUCT:
311 case ddf_body_t::DDF_LIST:
312 return m_handle->value.children.count;
318 void* DDF::pointer() const
320 return ispointer() ? m_handle->value.pointer : nullptr;
323 size_t DDF::strlen() const
325 return ddf_strlen(string());
328 bool DDF::operator==(const char* s) const
330 if (string()==nullptr || s==nullptr)
331 return (string()==nullptr && s==nullptr);
333 return (::strcmp(string(),s)==0);
339 switch (m_handle->type) {
340 case ddf_body_t::DDF_STRING:
341 case ddf_body_t::DDF_STRING_UNSAFE:
342 if (m_handle->value.string)
343 free(m_handle->value.string);
345 case ddf_body_t::DDF_LIST:
346 case ddf_body_t::DDF_STRUCT:
349 while (m_handle->value.children.first)
351 temp.m_handle=m_handle->value.children.first;
356 m_handle->type=ddf_body_t::DDF_EMPTY;
361 DDF& DDF::string(char* val, bool copyit, bool safe)
363 if (empty().m_handle) {
364 m_handle->value.string = copyit ? ddf_strdup(val) : val;
365 if (!m_handle->value.string && val && *val)
367 m_handle->type=(safe ? ddf_body_t::DDF_STRING : ddf_body_t::DDF_STRING_UNSAFE);
372 DDF& DDF::string(const char* val)
374 return string(const_cast<char*>(val), true);
377 DDF& DDF::unsafe_string(const char* val)
379 return string(const_cast<char*>(val), true, false);
382 DDF& DDF::string(long val)
386 sprintf(buf,"%ld",val);
390 DDF& DDF::string(double val)
394 snprintf(buf,39,"%f",val);
398 DDF& DDF::integer(long val)
400 if (empty().m_handle) {
401 m_handle->value.integer=val;
402 m_handle->type=ddf_body_t::DDF_INT;
407 DDF& DDF::integer(const char* val)
409 if (empty().m_handle) {
410 m_handle->value.integer=(val ? atol(val) : 0);
411 m_handle->type=ddf_body_t::DDF_INT;
416 DDF& DDF::floating(double val)
418 if (empty().m_handle) {
419 m_handle->value.floating=val;
420 m_handle->type=ddf_body_t::DDF_FLOAT;
425 DDF& DDF::floating(const char* val)
427 if (empty().m_handle) {
428 m_handle->value.floating=(val ? atof(val) : 0);
429 m_handle->type=ddf_body_t::DDF_FLOAT;
434 DDF& DDF::structure()
436 if (empty().m_handle) {
437 m_handle->type=ddf_body_t::DDF_STRUCT;
438 m_handle->value.children.first=nullptr;
439 m_handle->value.children.last=nullptr;
440 m_handle->value.children.current=nullptr;
441 m_handle->value.children.count=0;
448 if (empty().m_handle) {
449 m_handle->type=ddf_body_t::DDF_LIST;
450 m_handle->value.children.first=nullptr;
451 m_handle->value.children.last=nullptr;
452 m_handle->value.children.current=nullptr;
453 m_handle->value.children.count=0;
458 DDF& DDF::pointer(void* val)
460 if (empty().m_handle) {
461 m_handle->value.pointer=val;
462 m_handle->type=ddf_body_t::DDF_POINTER;
467 DDF& DDF::add(DDF& child)
469 if ((!isstruct() && !islist()) || !child.m_handle)
472 if (m_handle==child.m_handle->parent)
478 getmember(child.name()).destroy();
482 if (!m_handle->value.children.first)
483 m_handle->value.children.first=child.m_handle;
485 m_handle->value.children.last->next=child.m_handle;
486 child.m_handle->prev=m_handle->value.children.last;
488 m_handle->value.children.last=child.m_handle;
489 child.m_handle->parent=m_handle;
490 m_handle->value.children.count++;
494 DDF& DDF::addbefore(DDF& child, DDF& before)
496 if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
500 if (m_handle->value.children.first==before.m_handle)
501 m_handle->value.children.first=child.m_handle;
502 child.m_handle->prev=before.m_handle->prev;
503 if (child.m_handle->prev)
504 child.m_handle->prev->next=child.m_handle;
505 before.m_handle->prev=child.m_handle;
506 child.m_handle->next=before.m_handle;
507 child.m_handle->parent=m_handle;
508 m_handle->value.children.count++;
512 DDF& DDF::addafter(DDF& child, DDF& after)
514 if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
518 if (m_handle->value.children.last==after.m_handle)
519 m_handle->value.children.last=child.m_handle;
520 child.m_handle->next=after.m_handle->next;
521 if (child.m_handle->next)
522 child.m_handle->next->prev=child.m_handle;
523 after.m_handle->next=child.m_handle;
524 child.m_handle->prev=after.m_handle;
525 child.m_handle->parent=m_handle;
526 m_handle->value.children.count++;
530 void DDF::swap(DDF& arg)
532 ddf_body_t* temp=arg.m_handle;
533 arg.m_handle=m_handle;
539 if (!m_handle || !m_handle->parent)
543 m_handle->next->prev=m_handle->prev;
546 m_handle->prev->next=m_handle->next;
548 if (m_handle->parent->value.children.first==m_handle)
549 m_handle->parent->value.children.first=m_handle->next;
551 if (m_handle->parent->value.children.last==m_handle)
552 m_handle->parent->value.children.last=m_handle->prev;
554 if (m_handle->parent->value.children.current==m_handle)
555 m_handle->parent->value.children.current=m_handle->prev;
557 m_handle->parent->value.children.count--;
558 m_handle->parent=nullptr;
559 m_handle->next=nullptr;
560 m_handle->prev=nullptr;
564 DDF DDF::parent() const
568 p.m_handle=(m_handle ? m_handle->parent : nullptr);
576 if (islist() || isstruct())
577 f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
585 if ((islist() || isstruct()) && m_handle->value.children.current!=m_handle->value.children.last) {
586 if (!m_handle->value.children.current)
587 n.m_handle=m_handle->value.children.current=m_handle->value.children.first;
589 n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
598 if ((islist() || isstruct()) && m_handle->value.children.last) {
599 m_handle->value.children.current=m_handle->value.children.last->prev;
600 l.m_handle=m_handle->value.children.last;
609 if (islist() || isstruct()) {
610 p.m_handle=m_handle->value.children.current;
612 m_handle->value.children.current=m_handle->value.children.current->prev;
617 DDF DDF::operator[](const char* path) const
619 return getmember(path);
622 DDF DDF::operator[](unsigned long index) const
626 if (islist() && index<m_handle->value.children.count) {
627 for (d.m_handle=m_handle->value.children.first; index; index--)
628 d.m_handle=d.m_handle->next;
631 throw range_error("DDF object not a list with >=index+1 elements");
635 DDF DDF::addmember(const char* path)
637 char name[MAX_NAME_LEN+1];
638 const char* path_ptr=path;
640 if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
644 DDF new_member=getmember(name);
645 if (!new_member.m_handle) {
647 new_member=add(temp);
650 if (new_member.m_handle) {
651 if (ddf_strlen(path_ptr)>0) {
652 DDF last_member=new_member.addmember(path_ptr);
653 if (!last_member.m_handle)
654 return new_member.destroy();
665 DDF DDF::getmember(const char* path) const
668 char name[MAX_NAME_LEN+1];
669 const char* path_ptr=path;
671 ddf_token(&path_ptr, name);
674 else if (*name == '[') {
675 unsigned long i = strtoul(name+1, nullptr, 10);
676 if (islist() && i < m_handle->value.children.count)
677 current=operator[](i);
681 else if (isstruct()) {
682 current.m_handle = m_handle->value.children.first;
683 while (current.m_handle && strcmp(current.m_handle->name,name) != 0)
684 current.m_handle = current.m_handle->next;
687 current.m_handle = m_handle->value.children.first;
688 return current.getmember(path);
691 if (current.m_handle && path_ptr && *path_ptr)
692 current = current.getmember(path_ptr);
697 void ddf_print_indent(FILE* f, int indent)
699 for (; indent>0; indent--)
703 void DDF::dump(FILE* f, int indent) const
708 ddf_print_indent(f,indent);
710 switch (m_handle->type) {
712 case ddf_body_t::DDF_EMPTY:
715 fprintf(f," %s",m_handle->name);
718 case ddf_body_t::DDF_STRING:
719 case ddf_body_t::DDF_STRING_UNSAFE:
721 fprintf(f,"char* %s = ",m_handle->name);
723 fprintf(f,"char* = ");
724 if (const char* chptr=m_handle->value.string) {
731 fprintf(f,"nullptr");
734 case ddf_body_t::DDF_INT:
736 fprintf(f,"long %s = ",m_handle->name);
738 fprintf(f,"long = ");
739 fprintf(f,"%ld",m_handle->value.integer);
742 case ddf_body_t::DDF_FLOAT:
744 fprintf(f,"double %s = ",m_handle->name);
746 fprintf(f,"double = ");
747 fprintf(f,"%.15f",m_handle->value.floating);
750 case ddf_body_t::DDF_STRUCT:
751 fprintf(f,"struct ");
753 fprintf(f,"%s ",m_handle->name);
755 if (m_handle->value.children.count) {
758 child.m_handle=m_handle->value.children.first;
759 while (child.m_handle) {
760 child.dump(f,indent+2);
761 child.m_handle=child.m_handle->next;
764 ddf_print_indent(f,indent);
768 case ddf_body_t::DDF_LIST:
771 fprintf(f," %s",m_handle->name);
772 fprintf(f,"[%lu] {",m_handle->value.children.count);
773 if (m_handle->value.children.count) {
776 child.m_handle=m_handle->value.children.first;
777 while (child.m_handle) {
778 child.dump(f,indent+2);
779 child.m_handle=child.m_handle->next;
782 ddf_print_indent(f,indent);
786 case ddf_body_t::DDF_POINTER:
788 fprintf(f,"void* %s = ",m_handle->name);
790 fprintf(f,"void* = ");
791 if (m_handle->value.pointer)
792 fprintf(f,"%p",m_handle->value.pointer);
794 fprintf(f,"nullptr");
798 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
802 fprintf(f,"nullptr");
806 // Serialization is fairly easy. We have to walk the DDF and hand-generate a
807 // wddxPacket XML fragment, with some simple extensions. We escape the four major
808 // special characters, which requires that we output strings one char at a time.
810 void xml_encode(ostream& os, const char* start)
813 while (start && *start) {
814 pos = strcspn(start, "\"<>&");
821 case '"': os << """; break;
822 case '<': os << "<"; break;
823 case '>': os << ">"; break;
824 case '&': os << "&"; break;
825 default: os << *start;
832 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
837 case ddf_body_t::DDF_STRING:
838 case ddf_body_t::DDF_STRING_UNSAFE:
840 if (name_attr && p->name) {
842 xml_encode(os,p->name);
845 if (p->value.string) {
846 if (p->type == ddf_body_t::DDF_STRING) {
848 xml_encode(os,p->value.string);
851 os << " unsafe=\"1\">";
852 xml_encode(os,XMLToolingConfig::getConfig().getURLEncoder()->encode(p->value.string).c_str());
860 case ddf_body_t::DDF_INT:
862 if (name_attr && p->name) {
864 xml_encode(os,p->name);
867 os << '>' << p->value.integer << "</number>";
870 case ddf_body_t::DDF_FLOAT:
872 if (name_attr && p->name) {
874 xml_encode(os,p->name);
877 os << '>' << fixed << p->value.floating << dec << "</number>";
880 case ddf_body_t::DDF_STRUCT:
883 if (name_attr && p->name) {
885 xml_encode(os,p->name);
889 ddf_body_t* child=p->value.children.first;
891 os << "<var name=\"";
892 xml_encode(os,child->name);
894 serialize(child,os,false);
902 case ddf_body_t::DDF_LIST:
904 os << "<array length=\"" << p->value.children.count << '"';
905 if (name_attr && p->name) {
907 xml_encode(os,p->name);
911 ddf_body_t* child=p->value.children.first;
920 case ddf_body_t::DDF_EMPTY:
921 case ddf_body_t::DDF_POINTER:
924 if (name_attr && p->name) {
926 xml_encode(os,p->name);
937 // The stream insertion will work for any ostream-based object.
939 SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
942 os << "<wddxPacket version=\"1.0\" lowercase=\"no\"><header/><data>";
943 serialize(obj.m_handle,os);
944 os << "</data></wddxPacket>";
948 // This is a DTD internal subset based on a compatible permutation of the WDDX spec, with the
949 // extension of a name attribute on all the typed elements, which DDF has, but WDDX does not.
952 static const char* g_DocType=
954 <!DOCTYPE wddxPacket [\n\
955 <!ELEMENT wddxPacket (header, data)>\n\
956 <!ATTLIST wddxPacket version CDATA #FIXED \"1.0\" lowercase (yes|no) \"yes\">\n\
957 <!ELEMENT header (comment?)>\n\
958 <!ELEMENT comment (#PCDATA)>\n\
959 <!ELEMENT data (null | number | string | array | struct)>\n\
960 <!ELEMENT null EMPTY>\n\
961 <!ATTLIST null name CDATA #IMPLIED type CDATA #IMPLIED>\n\
962 <!ELEMENT string (#PCDATA | char)*>\n\
963 <!ATTLIST string name CDATA #IMPLIED type CDATA #IMPLIED>\n\
964 <!ELEMENT char EMPTY>\n\
965 <!ATTLIST char code CDATA #REQUIRED>\n\
966 <!ELEMENT number (#PCDATA)>\n\
967 <!ATTLIST number name CDATA #IMPLIED type CDATA #IMPLIED>\n\
968 <!ELEMENT array (null | number | string | array | struct)*>\n\
969 <!ATTLIST array length CDATA #REQUIRED name CDATA #IMPLIED type CDATA #IMPLIED>\n\
970 <!ELEMENT struct (var*)>\n\
971 <!ATTLIST struct name CDATA #IMPLIED type CDATA #IMPLIED>\n\
972 <!ELEMENT var (null | number | string | array | struct)>\n\
973 <!ATTLIST var name CDATA #REQUIRED>\n\
977 // This function constructs a DDF object equivalent to the wddx data element rooted
980 static const XMLCh _no[] = UNICODE_LITERAL_2(n,o);
981 static const XMLCh _name[] = UNICODE_LITERAL_4(n,a,m,e);
982 static const XMLCh _var[] = UNICODE_LITERAL_3(v,a,r);
983 static const XMLCh _string[] = UNICODE_LITERAL_6(s,t,r,i,n,g);
984 static const XMLCh _number[] = UNICODE_LITERAL_6(n,u,m,b,e,r);
985 static const XMLCh _array[] = UNICODE_LITERAL_5(a,r,r,a,y);
986 static const XMLCh _struct[] = UNICODE_LITERAL_6(s,t,r,u,c,t);
987 static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e);
988 static const XMLCh _unsafe[] = UNICODE_LITERAL_6(u,n,s,a,f,e);
990 DDF deserialize(DOMElement* root, bool lowercase)
993 auto_ptr_char name_val(root->getAttributeNS(nullptr, _name));
994 if (name_val.get() && *name_val.get()) {
996 for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
997 obj.name(name_val.get());
1000 const XMLCh* tag=root->getTagName();
1001 if (XMLString::equals(tag,_var)) {
1002 root=XMLHelper::getFirstChildElement(root);
1003 tag=(root ? root->getTagName() : &chNull);
1006 if (XMLString::equals(tag,_string)) {
1007 DOMNode* child=root->getFirstChild();
1008 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
1009 const XMLCh* unsafe = root->getAttributeNS(nullptr, _unsafe);
1010 if (unsafe && *unsafe==chDigit_1) {
1011 // If it's unsafe, it's not UTF-8 data, so we have to convert to ASCII and decode it.
1012 char* encoded = XMLString::transcode(child->getNodeValue());
1013 XMLToolingConfig::getConfig().getURLEncoder()->decode(encoded);
1014 obj.string(encoded, true, false); // re-copy into free-able buffer, plus mark unsafe
1015 XMLString::release(&encoded);
1018 char* val = toUTF8(child->getNodeValue(), true); // use malloc
1019 obj.string(val, false); // don't re-copy the string
1023 else if (XMLString::equals(tag,_number)) {
1024 DOMNode* child=root->getFirstChild();
1025 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
1026 auto_ptr_char val(child->getNodeValue());
1027 if (val.get() && strchr(val.get(),'.'))
1028 obj.floating(val.get());
1030 obj.integer(val.get());
1033 else if (XMLString::equals(tag,_array)) {
1035 DOMNodeList* children=root->getChildNodes();
1036 for (unsigned int i=0; children && i<children->getLength(); i++)
1037 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
1038 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
1042 else if (XMLString::equals(tag,_struct)) {
1044 DOMNodeList* children=root->getChildNodes();
1045 for (unsigned int i=0; children && i<children->getLength(); i++)
1046 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
1047 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
1055 SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
1057 // Parse the input stream into a DOM tree and construct the equivalent DDF.
1058 DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(is);
1059 XercesJanitor<DOMDocument> docj(doc);
1060 const XMLCh* lowercase=doc->getDocumentElement()->getAttribute(_lowercase);
1061 DOMElement* first=XMLHelper::getFirstChildElement(XMLHelper::getLastChildElement(doc->getDocumentElement()));
1063 obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);