463bb8c06378784ad4bceb455de4820f726c90d5
[shibboleth/sp.git] / shibsp / remoting / impl / ddf.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  * ddf.cpp
19  * 
20  * C++ DDF abstraction for interpretive RPC
21  */
22
23 #include "internal.h"
24 #include "remoting/ddf.h"
25
26 #ifdef WIN32
27 # define snprintf _snprintf
28 #endif
29
30 #include <stdexcept>
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/XMLHelper.h>
36
37 using namespace shibsp;
38 using namespace xmltooling;
39 using namespace xercesc;
40 using namespace std;
41
42 // defensive string functions
43
44 size_t ddf_strlen(const char* s)
45 {
46     return s ? strlen(s) : 0;
47 }
48
49 char* ddf_strdup(const char* s)
50 {
51     return (s && *s) ? strdup(s) : NULL;
52 }
53
54 #define MAX_NAME_LEN 255
55
56 /* Parses '.' notation paths, where each component is at most MAX_NAME_LEN long.
57    path contains the address of a constant string which is the current path.
58    name points to a buffer in which to place the first path component.
59    After execution, the path pointer will be moved past the first dot.
60    The actual path string is never modified. Only name is written to.
61    The name buffer is returned from the function. */
62 char* ddf_token(const char** path, char* name)
63 {
64     const char* temp=NULL;
65     
66     *name='\0';
67     if (*path==NULL || **path=='\0')
68         return name;
69
70     temp=strchr(*path,'.');
71     if (temp==NULL)
72     {
73         strcpy(name,*path);
74         *path=NULL;
75     }
76     else if (temp>*path)
77     {
78         strncpy(name,*path,temp-*path);
79         name[temp-*path]='\0';
80         *path=temp+1;
81     }
82     else
83         *path=temp+1;
84     return name;
85 }
86
87 // body implementation
88
89 struct shibsp::ddf_body_t {
90     ddf_body_t() : name(NULL), parent(NULL), next(NULL), prev(NULL), type(DDF_EMPTY) {}
91
92     char* name;                     // name of node
93     ddf_body_t* parent;             // parent node, if any
94     ddf_body_t* next;               // next node, if any
95     ddf_body_t* prev;               // previous node, if any
96
97     enum {
98             DDF_EMPTY,
99             DDF_STRING,
100             DDF_INT,
101         DDF_FLOAT,
102             DDF_STRUCT,
103         DDF_LIST,
104             DDF_POINTER
105     } type;                         // data type of node
106
107     union {
108         char* string;
109         long integer;
110         double floating;
111         void* pointer;
112         struct {
113                 ddf_body_t* first;
114                 ddf_body_t* last;
115                 ddf_body_t* current;
116                 unsigned long count;
117         } children;
118     } value;                        // value of node
119 };
120
121 // library implementation
122
123 DDF::DDF(const char* n)
124 {
125     m_handle=new(nothrow) ddf_body_t;
126     name(n);
127 }
128
129 DDF::DDF(const char* n, const char* val)
130 {
131     m_handle=new(nothrow) ddf_body_t;
132     name(n);
133     string(val);
134 }
135
136 DDF::DDF(const char* n, long val)
137 {
138     m_handle=new(nothrow) ddf_body_t;
139     name(n);
140     integer(val);
141 }
142
143 DDF::DDF(const char* n, double val)
144 {
145     m_handle=new(nothrow) ddf_body_t;
146     name(n);
147     floating(val);
148 }
149
150 DDF::DDF(const char* n, void* val)
151 {
152     m_handle=new(nothrow) ddf_body_t;
153     name(n);
154     pointer(val);
155 }
156
157 DDF& DDF::destroy()
158 {
159     remove().empty().name(NULL);
160     delete m_handle;
161     m_handle=NULL;
162     return *this;
163 }
164
165 DDF DDF::copy() const
166 {
167     if (m_handle==NULL)
168         return DDF();
169
170     switch (m_handle->type) {
171         case ddf_body_t::DDF_EMPTY:
172             return DDF(m_handle->name);
173         case ddf_body_t::DDF_STRING:
174             return DDF(m_handle->name,m_handle->value.string);
175         case ddf_body_t::DDF_INT:
176             return DDF(m_handle->name,m_handle->value.integer);
177         case ddf_body_t::DDF_FLOAT:
178             return DDF(m_handle->name,m_handle->value.floating);
179         case ddf_body_t::DDF_POINTER:
180             return DDF(m_handle->name,m_handle->value.pointer);
181         case ddf_body_t::DDF_STRUCT:
182         case ddf_body_t::DDF_LIST:
183         {
184             DDF copy(m_handle->name), temp;
185             if (m_handle->type==ddf_body_t::DDF_STRUCT)
186                 copy.structure();
187             else
188                 copy.list();
189             ddf_body_t* child=m_handle->value.children.first;
190             while (child) {
191                 temp.m_handle=child;
192                 DDF temp2=temp.copy();
193                 copy.add(temp2);
194                 if (copy.m_handle==NULL)
195                     return copy;
196                 if (m_handle->value.children.current==child)
197                     copy.m_handle->value.children.current=copy.m_handle->value.children.last;
198                 child=child->next;
199             }
200             return copy;
201         }
202     }
203     return DDF();
204 }
205
206 const char* DDF::name() const
207 {
208     return (m_handle) ? m_handle->name : NULL;
209 }
210
211 DDF& DDF::name(const char* name)
212 {
213     char trunc_name[MAX_NAME_LEN+1]="";
214
215     if (m_handle) {
216         if (m_handle->name)
217             free(m_handle->name);
218         if (name && *name) {
219             strncpy(trunc_name,name,MAX_NAME_LEN);
220             trunc_name[MAX_NAME_LEN]='\0';
221             m_handle->name=ddf_strdup(trunc_name);
222             if (!m_handle->name)
223                 destroy();
224         }
225         else
226             m_handle->name=NULL;
227     }
228     return *this;
229 }
230
231 bool DDF::isnull() const
232 {
233     return m_handle ? false : true;
234 }
235
236 bool DDF::isempty() const
237 {
238     return m_handle ? (m_handle->type==ddf_body_t::DDF_EMPTY) : false;
239 }
240
241 bool DDF::isstring() const
242 {
243     return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING) : false;
244 }
245
246 bool DDF::isint() const
247 {
248     return m_handle ? (m_handle->type==ddf_body_t::DDF_INT) : false;
249 }
250
251 bool DDF::isfloat() const
252 {
253     return m_handle ? (m_handle->type==ddf_body_t::DDF_FLOAT) : false;
254 }
255
256 bool DDF::isstruct() const
257 {
258     return m_handle ? (m_handle->type==ddf_body_t::DDF_STRUCT) : false;
259 }
260
261 bool DDF::islist() const
262 {
263     return m_handle ? (m_handle->type==ddf_body_t::DDF_LIST) : false;
264 }
265
266 bool DDF::ispointer() const
267 {
268     return m_handle ? (m_handle->type==ddf_body_t::DDF_POINTER) : false;
269 }
270
271 const char* DDF::string() const
272 {
273     return isstring() ? m_handle->value.string : NULL;
274 }
275
276 long DDF::integer() const
277 {
278     if (m_handle) {
279         switch(m_handle->type) {
280             case ddf_body_t::DDF_INT:
281                 return m_handle->value.integer;
282             case ddf_body_t::DDF_FLOAT:
283                 return static_cast<long>(m_handle->value.floating);
284             case ddf_body_t::DDF_STRING:
285                 return m_handle->value.string ? atol(m_handle->value.string) : 0;
286             case ddf_body_t::DDF_STRUCT:
287             case ddf_body_t::DDF_LIST:
288                 return m_handle->value.children.count;
289         }
290     }
291     return 0;
292 }
293
294 double DDF::floating() const
295 {
296     if (m_handle) {
297         switch(m_handle->type) {
298             case ddf_body_t::DDF_INT:
299                 return m_handle->value.integer;
300             case ddf_body_t::DDF_FLOAT:
301                 return m_handle->value.floating;
302             case ddf_body_t::DDF_STRING:
303                 return m_handle->value.string ? atof(m_handle->value.string) : 0;
304             case ddf_body_t::DDF_STRUCT:
305             case ddf_body_t::DDF_LIST:
306                 return m_handle->value.children.count;
307         }
308     }
309     return 0;
310 }
311
312 void* DDF::pointer() const
313 {
314     return ispointer() ? m_handle->value.pointer : NULL;
315 }
316
317 size_t DDF::strlen() const
318 {
319     return ddf_strlen(string());
320 }
321
322 bool DDF::operator==(const char* s) const
323 {
324     if (string()==NULL || s==NULL)
325         return (string()==NULL && s==NULL);
326     else
327         return (::strcmp(string(),s)==0);
328 }
329
330 DDF& DDF::empty()
331 {
332     if (m_handle) {
333         switch (m_handle->type) {
334             case ddf_body_t::DDF_STRING:
335                 if (m_handle->value.string)
336                     free(m_handle->value.string);
337                 break;
338             case ddf_body_t::DDF_LIST:
339             case ddf_body_t::DDF_STRUCT:
340             {
341                 DDF temp;
342                 while (m_handle->value.children.first)
343                 {
344                     temp.m_handle=m_handle->value.children.first;
345                     temp.destroy();
346                 }
347             }
348         }
349         m_handle->type=ddf_body_t::DDF_EMPTY;
350     }
351     return *this;
352 }
353
354 DDF& DDF::string(char* val, bool copyit)
355 {
356     if (empty().m_handle) {
357         m_handle->value.string = copyit ? ddf_strdup(val) : val;
358         if (!m_handle->value.string && val && *val)
359             return destroy();
360         m_handle->type=ddf_body_t::DDF_STRING;
361     }
362     return *this;
363 }
364
365 DDF& DDF::string(long val)
366 {
367     char buf[20];
368
369     sprintf(buf,"%ld",val);
370     return string(buf);
371 }
372
373 DDF& DDF::string(double val)
374 {
375     char buf[40];
376
377     snprintf(buf,39,"%f",val);
378     return string(buf);
379 }
380
381 DDF& DDF::integer(long val)
382 {
383     if (empty().m_handle) {
384         m_handle->value.integer=val;
385         m_handle->type=ddf_body_t::DDF_INT;
386     }
387     return *this;
388 }
389
390 DDF& DDF::integer(const char* val)
391 {
392     if (empty().m_handle) {
393         m_handle->value.integer=(val ? atol(val) : 0);
394         m_handle->type=ddf_body_t::DDF_INT;
395     }
396     return *this;
397 }
398
399 DDF& DDF::floating(double val)
400 {
401     if (empty().m_handle) {
402         m_handle->value.floating=val;
403         m_handle->type=ddf_body_t::DDF_FLOAT;
404     }
405     return *this;
406 }
407
408 DDF& DDF::floating(const char* val)
409 {
410     if (empty().m_handle) {
411         m_handle->value.floating=(val ? atof(val) : 0);
412         m_handle->type=ddf_body_t::DDF_FLOAT;
413     }
414     return *this;
415 }
416
417 DDF& DDF::structure()
418 {
419     if (empty().m_handle) {
420         m_handle->type=ddf_body_t::DDF_STRUCT;
421         m_handle->value.children.first=NULL;
422         m_handle->value.children.last=NULL;
423         m_handle->value.children.current=NULL;
424         m_handle->value.children.count=0;
425     }
426     return *this;
427 }
428
429 DDF& DDF::list()
430 {
431     if (empty().m_handle) {
432         m_handle->type=ddf_body_t::DDF_LIST;
433         m_handle->value.children.first=NULL;
434         m_handle->value.children.last=NULL;
435         m_handle->value.children.current=NULL;
436         m_handle->value.children.count=0;
437     }
438     return *this;
439 }
440
441 DDF& DDF::pointer(void* val)
442 {
443     if (empty().m_handle) {
444         m_handle->value.pointer=val;
445         m_handle->type=ddf_body_t::DDF_POINTER;
446     }
447     return *this;
448 }
449
450 DDF& DDF::add(DDF& child)
451 {
452     if ((!isstruct() && !islist()) || !child.m_handle)
453         return child;
454
455     if (m_handle==child.m_handle->parent)
456         return child;
457
458     if (isstruct()) {
459         if (!child.name())
460             return child;
461         getmember(child.name()).destroy();
462     }
463
464     child.remove();
465     if (!m_handle->value.children.first)
466         m_handle->value.children.first=child.m_handle;
467     else {
468         m_handle->value.children.last->next=child.m_handle;
469         child.m_handle->prev=m_handle->value.children.last;
470     }
471     m_handle->value.children.last=child.m_handle;
472     child.m_handle->parent=m_handle;
473     m_handle->value.children.count++;
474     return child;
475 }
476
477 DDF& DDF::addbefore(DDF& child, DDF& before)
478 {
479     if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
480         return child;
481
482     child.remove();
483     if (m_handle->value.children.first==before.m_handle)
484         m_handle->value.children.first=child.m_handle;
485     child.m_handle->prev=before.m_handle->prev;
486     if (child.m_handle->prev)
487         child.m_handle->prev->next=child.m_handle;
488     before.m_handle->prev=child.m_handle;
489     child.m_handle->next=before.m_handle;
490     child.m_handle->parent=m_handle;
491     m_handle->value.children.count++;
492     return child;
493 }
494
495 DDF& DDF::addafter(DDF& child, DDF& after)
496 {
497     if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
498         return child;
499
500     child.remove();
501     if (m_handle->value.children.last==after.m_handle)
502         m_handle->value.children.last=child.m_handle;
503     child.m_handle->next=after.m_handle->next;
504     if (child.m_handle->next)
505         child.m_handle->next->prev=child.m_handle;
506     after.m_handle->next=child.m_handle;
507     child.m_handle->prev=after.m_handle;
508     child.m_handle->parent=m_handle;
509     m_handle->value.children.count++;
510     return child;
511 }
512
513 void DDF::swap(DDF& arg)
514 {
515     ddf_body_t* temp=arg.m_handle;
516     arg.m_handle=m_handle;
517     m_handle=temp;
518 }
519
520 DDF& DDF::remove()
521 {
522     if (!m_handle || !m_handle->parent)
523         return *this;
524
525     if (m_handle->next)
526         m_handle->next->prev=m_handle->prev;
527
528     if (m_handle->prev)
529         m_handle->prev->next=m_handle->next;
530
531     if (m_handle->parent->value.children.first==m_handle)
532         m_handle->parent->value.children.first=m_handle->next;
533
534     if (m_handle->parent->value.children.last==m_handle)
535         m_handle->parent->value.children.last=m_handle->prev;
536
537     if (m_handle->parent->value.children.current==m_handle)
538         m_handle->parent->value.children.current=m_handle->prev;
539
540     m_handle->parent->value.children.count--;
541     m_handle->parent=NULL;
542     m_handle->next=NULL;
543     m_handle->prev=NULL;
544     return *this;
545 }
546
547 DDF DDF::parent() const
548 {
549     DDF p;
550
551     p.m_handle=(m_handle ? m_handle->parent : NULL);
552     return p;
553 }
554
555 DDF DDF::first()
556 {
557     DDF f;
558
559     if (islist() || isstruct())
560         f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
561     return f;
562 }
563
564 DDF DDF::next()
565 {
566     DDF n;
567
568     if ((islist() || isstruct()) && m_handle->value.children.current!=m_handle->value.children.last) {
569         if (!m_handle->value.children.current)
570             n.m_handle=m_handle->value.children.current=m_handle->value.children.first;
571         else
572             n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
573     }
574     return n;
575 }
576
577 DDF DDF::last()
578 {
579     DDF l;
580
581     if ((islist() || isstruct()) && m_handle->value.children.last) {
582         m_handle->value.children.current=m_handle->value.children.last->prev;
583         l.m_handle=m_handle->value.children.last;
584     }
585     return l;
586 }
587
588 DDF DDF::previous()
589 {
590     DDF p;
591
592     if (islist() || isstruct()) {
593         p.m_handle=m_handle->value.children.current;
594         if (p.m_handle)
595             m_handle->value.children.current=m_handle->value.children.current->prev;
596     }
597     return p;
598 }
599
600 DDF DDF::operator[](unsigned long index) const
601 {
602     DDF d;
603
604     if (islist() && index<m_handle->value.children.count) {
605         for (d.m_handle=m_handle->value.children.first; index; index--)
606             d.m_handle=d.m_handle->next;
607     }
608     else
609         throw range_error("DDF object not a list with >=index+1 elements");
610     return d;
611 }
612
613 DDF DDF::addmember(const char* path)
614 {
615     char name[MAX_NAME_LEN+1];
616     const char* path_ptr=path;
617     
618     if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
619         if (!isstruct())
620             structure();
621
622         DDF new_member=getmember(name);
623         if (!new_member.m_handle) {
624             DDF temp(name);
625             new_member=add(temp);
626         }
627
628         if (new_member.m_handle) {
629             if (ddf_strlen(path_ptr)>0) {
630                 DDF last_member=new_member.addmember(path_ptr);
631                 if (!last_member.m_handle)
632                     return new_member.destroy();
633                 else
634                     return last_member;
635             }
636             return new_member;
637         }
638         return new_member;
639     }
640     return DDF();
641 }
642
643 DDF DDF::getmember(const char* path) const
644 {
645     char name[MAX_NAME_LEN+1];
646     const char* path_ptr=path;
647     DDF current;
648
649     if (isstruct() && ddf_strlen(ddf_token(&path_ptr,name))>0) {
650         current.m_handle=m_handle->value.children.first;
651         while (current.m_handle && strcmp(current.m_handle->name,name)!=0)
652             current.m_handle=current.m_handle->next;
653
654         if (current.m_handle && ddf_strlen(path_ptr)>0)
655             current=current.getmember(path_ptr);
656     }
657     return current;
658 }
659
660
661 void ddf_print_indent(FILE* f, int indent)
662 {
663     for (; indent>0; indent--)
664         putc(' ',f);
665 }
666
667 void DDF::dump(FILE* f, int indent) const
668 {
669     if (!f)
670         f=stderr;
671
672     ddf_print_indent(f,indent);
673     if (m_handle) {
674         switch (m_handle->type) {
675             
676             case ddf_body_t::DDF_EMPTY:
677                 fprintf(f,"empty");
678                 if (m_handle->name)
679                     fprintf(f," %s",m_handle->name);
680                 break;
681
682             case ddf_body_t::DDF_STRING:
683                 if (m_handle->name)
684                     fprintf(f,"char* %s = ",m_handle->name);
685                 else
686                     fprintf(f,"char* = ");
687                 if (const char* chptr=m_handle->value.string) {
688                     putc('"',f);
689                     while (*chptr)
690                         fputc(*chptr++,f);
691                     putc('"',f);
692                 }
693                 else
694                     fprintf(f,"NULL");
695                 break;
696
697             case ddf_body_t::DDF_INT:
698                 if (m_handle->name)
699                     fprintf(f,"long %s = ",m_handle->name);
700                 else
701                     fprintf(f,"long = ");
702                 fprintf(f,"%ld",m_handle->value.integer);
703                 break;
704
705             case ddf_body_t::DDF_FLOAT:
706                 if (m_handle->name)
707                     fprintf(f,"double %s = ",m_handle->name);
708                 else
709                     fprintf(f,"double = ");
710                 fprintf(f,"%.15f",m_handle->value.floating);
711                 break;
712
713             case ddf_body_t::DDF_STRUCT:
714                 fprintf(f,"struct ");
715                 if (m_handle->name)
716                     fprintf(f,"%s ",m_handle->name);
717                 putc('{',f);
718                 if (m_handle->value.children.count) {
719                     putc('\n',f);
720                     DDF child;
721                     child.m_handle=m_handle->value.children.first;
722                     while (child.m_handle) {
723                         child.dump(f,indent+2);
724                         child.m_handle=child.m_handle->next;
725                     }
726                 }
727                 ddf_print_indent(f,indent);
728                 putc('}',f);
729                 break;
730
731             case ddf_body_t::DDF_LIST:
732                 fprintf(f,"list");
733                 if (m_handle->name)
734                     fprintf(f," %s",m_handle->name);
735                 fprintf(f,"[%lu] {",m_handle->value.children.count);
736                 if (m_handle->value.children.count) {
737                     putc('\n',f);
738                     DDF child;
739                     child.m_handle=m_handle->value.children.first;
740                     while (child.m_handle) {
741                         child.dump(f,indent+2);
742                         child.m_handle=child.m_handle->next;
743                     }
744                 }
745                 ddf_print_indent(f,indent);
746                 putc('}',f);
747                 break;
748
749             case ddf_body_t::DDF_POINTER:
750                 if (m_handle->name)
751                     fprintf(f,"void* %s = ",m_handle->name);
752                 else
753                     fprintf(f,"void* = ");
754                 if (m_handle->value.pointer)
755                     fprintf(f,"%p",m_handle->value.pointer);
756                 else
757                     fprintf(f,"NULL");
758                 break;
759
760             default:
761                 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
762         }
763     }
764     else
765         fprintf(f,"NULL");
766     fprintf(f,";\n");
767 }
768
769 // Serialization is fairly easy. We have to walk the DDF and hand-generate a
770 // wddxPacket XML fragment, with some simple extensions. We escape the four major
771 // special characters, which requires that we output strings one char at a time.
772
773 void xml_encode(ostream& os, const char* start)
774 {
775     size_t pos;
776     while (start && *start) {
777         pos = strcspn(start, "\"<>&");
778         if (pos > 0) {
779             os.write(start,pos);
780             start += pos;
781         }
782         else {
783             switch (*start) {
784                 case '"':   os << "&quot;";     break;
785                 case '<':   os << "&lt;";       break;
786                 case '>':   os << "&gt;";       break;
787                 case '&':   os << "&amp;";      break;
788                 default:    os << *start;
789             }
790             start++;
791         }
792     }
793 }
794
795 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
796 {
797     if (p) {
798         switch (p->type) {
799             
800             case ddf_body_t::DDF_STRING:
801                 os << "<string";
802                 if (name_attr && p->name) {
803                     os << " name=\"";
804                     xml_encode(os,p->name);
805                     os << '"';
806                 }
807                 if (p->value.string) {
808                     os << '>';
809                     xml_encode(os,p->value.string);
810                     os << "</string>";
811                 }
812                 else
813                     os << "/>";
814                 break;
815
816             case ddf_body_t::DDF_INT:
817                 os << "<number";
818                 if (name_attr && p->name) {
819                     os << " name=\"";
820                     xml_encode(os,p->name);
821                     os << '"';
822                 }
823                 os << '>' << p->value.integer << "</number>";
824                 break;
825
826             case ddf_body_t::DDF_FLOAT:
827                 os << "<number";
828                 if (name_attr && p->name) {
829                     os << " name=\"";
830                     xml_encode(os,p->name);
831                     os << '"';
832                 }
833                 os << '>' << fixed << p->value.floating << dec << "</number>";
834                 break;
835
836             case ddf_body_t::DDF_STRUCT:
837             {
838                 os << "<struct";
839                 if (name_attr && p->name) {
840                     os << " name=\"";
841                     xml_encode(os,p->name);
842                     os << '"';
843                 }
844                 os << '>';
845                 ddf_body_t* child=p->value.children.first;
846                 while (child) {
847                     os << "<var name=\"";
848                     xml_encode(os,child->name);
849                     os << "\">";
850                     serialize(child,os,false);
851                     os << "</var>";
852                     child=child->next;
853                 }
854                 os << "</struct>";
855                 break;
856             }
857
858             case ddf_body_t::DDF_LIST:
859             {
860                 os << "<array length=\"" << p->value.children.count << '"';
861                 if (name_attr && p->name) {
862                     os << " name=\"";
863                     xml_encode(os,p->name);
864                     os << '"';
865                 }
866                 os << '>';
867                 ddf_body_t* child=p->value.children.first;
868                 while (child) {
869                     serialize(child,os);
870                     child=child->next;
871                 }
872                 os << "</array>";
873                 break;
874             }
875
876             case ddf_body_t::DDF_EMPTY:
877             case ddf_body_t::DDF_POINTER:
878             default:
879                 os << "<null";
880                 if (name_attr && p->name) {
881                     os << " name=\"";
882                     xml_encode(os,p->name);
883                     os << '"';
884                 }
885                 os << "/>";
886                 break;
887         }
888     }
889     else
890         os << "<null/>";
891 }
892
893 // The stream insertion will work for any ostream-based object.
894
895 SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
896 {
897     os.precision(15);
898     os << "<wddxPacket version=\"1.0\" lowercase=\"no\"><header/><data>";
899     serialize(obj.m_handle,os);
900     os << "</data></wddxPacket>";
901     return os;
902 }
903
904 // This is a DTD internal subset based on a compatible permutation of the WDDX spec, with the
905 // extension of a name attribute on all the typed elements, which DDF has, but WDDX does not.
906
907 /*
908 static const char* g_DocType=
909 "\
910 <!DOCTYPE wddxPacket [\n\
911 <!ELEMENT wddxPacket (header, data)>\n\
912 <!ATTLIST wddxPacket version CDATA #FIXED \"1.0\" lowercase (yes|no) \"yes\">\n\
913 <!ELEMENT header (comment?)>\n\
914 <!ELEMENT comment (#PCDATA)>\n\
915 <!ELEMENT data (null | number | string | array | struct)>\n\
916 <!ELEMENT null EMPTY>\n\
917 <!ATTLIST null name CDATA #IMPLIED type CDATA #IMPLIED>\n\
918 <!ELEMENT string (#PCDATA | char)*>\n\
919 <!ATTLIST string name CDATA #IMPLIED type CDATA #IMPLIED>\n\
920 <!ELEMENT char EMPTY>\n\
921 <!ATTLIST char code CDATA #REQUIRED>\n\
922 <!ELEMENT number (#PCDATA)>\n\
923 <!ATTLIST number name CDATA #IMPLIED type CDATA #IMPLIED>\n\
924 <!ELEMENT array (null | number | string | array | struct)*>\n\
925 <!ATTLIST array length CDATA #REQUIRED name CDATA #IMPLIED type CDATA #IMPLIED>\n\
926 <!ELEMENT struct (var*)>\n\
927 <!ATTLIST struct name CDATA #IMPLIED type CDATA #IMPLIED>\n\
928 <!ELEMENT var (null | number | string | array | struct)>\n\
929 <!ATTLIST var name CDATA #REQUIRED>\n\
930 ]>\n";
931 */
932
933 // This function constructs a DDF object equivalent to the wddx data element rooted
934 // by the input.
935
936 static const XMLCh _no[] =      UNICODE_LITERAL_2(n,o);
937 static const XMLCh _name[] =    UNICODE_LITERAL_4(n,a,m,e);
938 static const XMLCh _var[] =     UNICODE_LITERAL_3(v,a,r);
939 static const XMLCh _string[] =  UNICODE_LITERAL_6(s,t,r,i,n,g);
940 static const XMLCh _number[] =  UNICODE_LITERAL_6(n,u,m,b,e,r);
941 static const XMLCh _array[] =   UNICODE_LITERAL_5(a,r,r,a,y);
942 static const XMLCh _struct[] =  UNICODE_LITERAL_6(s,t,r,u,c,t);
943 static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e);
944
945 DDF deserialize(DOMElement* root, bool lowercase)
946 {
947     DDF obj(NULL);
948     auto_ptr_char name_val(root->getAttribute(_name));
949     if (name_val.get() && *name_val.get()) {
950         if (lowercase)
951             for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
952         obj.name(name_val.get());
953     }
954
955     const XMLCh* tag=root->getTagName();
956     if (XMLString::equals(tag,_var)) {
957         root=XMLHelper::getFirstChildElement(root);
958         tag=(root ? root->getTagName() : &chNull);
959     }
960
961     if (XMLString::equals(tag,_string)) {
962         DOMNode* child=root->getFirstChild();
963         if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
964             char* val = toUTF8(child->getNodeValue(), true);    // use malloc
965             if (val)
966                 obj.string(val, false); // don't re-copy the string
967         }
968     }
969     else if (XMLString::equals(tag,_number)) {
970         DOMNode* child=root->getFirstChild();
971         if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
972             auto_ptr_char val(child->getNodeValue());
973             if (val.get() && strchr(val.get(),'.'))
974                 obj.floating(val.get());
975             else
976                 obj.integer(val.get());
977         }
978     }
979     else if (XMLString::equals(tag,_array)) {
980         obj.list();
981         DOMNodeList* children=root->getChildNodes();
982         for (unsigned int i=0; children && i<children->getLength(); i++)
983             if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
984                 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
985                 obj.add(temp);
986             }
987     }
988     else if (XMLString::equals(tag,_struct)) {
989         obj.structure();
990         DOMNodeList* children=root->getChildNodes();
991         for (unsigned int i=0; children && i<children->getLength(); i++)
992             if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
993                 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
994                 obj.add(temp);
995             }
996     }
997
998     return obj;
999 }
1000
1001 SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
1002 {
1003     // Parse the input stream into a DOM tree and construct the equivalent DDF.
1004     DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(is);
1005     XercesJanitor<DOMDocument> docj(doc);
1006     const XMLCh* lowercase=doc->getDocumentElement()->getAttribute(_lowercase);
1007     DOMElement* first=XMLHelper::getFirstChildElement(XMLHelper::getLastChildElement(doc->getDocumentElement()));
1008     obj.destroy();
1009     obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);
1010     return is;
1011 }