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