2 * Copyright 2001-2005 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
26 # define snprintf _snprintf
29 #include <shibsp/ddf.h>
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>
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) : NULL;
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)
65 const char* temp=NULL;
68 if (*path==NULL || **path=='\0')
71 temp=strchr(*path,'.');
79 strncpy(name,*path,temp-*path);
80 name[temp-*path]='\0';
88 // body implementation
90 struct shibsp::ddf_body_t {
91 ddf_body_t() : name(NULL), parent(NULL), next(NULL), prev(NULL), type(DDF_EMPTY) {}
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
106 } type; // data type of node
119 } value; // value of node
122 // library implementation
124 DDF::DDF(const char* n)
126 m_handle=new(nothrow) ddf_body_t;
130 DDF::DDF(const char* n, const char* val)
132 m_handle=new(nothrow) ddf_body_t;
137 DDF::DDF(const char* n, long val)
139 m_handle=new(nothrow) ddf_body_t;
144 DDF::DDF(const char* n, double val)
146 m_handle=new(nothrow) ddf_body_t;
151 DDF::DDF(const char* n, void* val)
153 m_handle=new(nothrow) ddf_body_t;
160 remove().empty().name(NULL);
166 DDF DDF::copy() const
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:
185 DDF copy(m_handle->name), temp;
186 if (m_handle->type==ddf_body_t::DDF_STRUCT)
190 ddf_body_t* child=m_handle->value.children.first;
193 DDF temp2=temp.copy();
195 if (copy.m_handle==NULL)
197 if (m_handle->value.children.current==child)
198 copy.m_handle->value.children.current=copy.m_handle->value.children.last;
207 const char* DDF::name() const
209 return (m_handle) ? m_handle->name : NULL;
212 DDF& DDF::name(const char* name)
214 char trunc_name[MAX_NAME_LEN+1]="";
218 free(m_handle->name);
220 strncpy(trunc_name,name,MAX_NAME_LEN);
221 trunc_name[MAX_NAME_LEN]='\0';
222 m_handle->name=ddf_strdup(trunc_name);
232 bool DDF::isnull() const
234 return m_handle ? false : true;
237 bool DDF::isempty() const
239 return m_handle ? (m_handle->type==ddf_body_t::DDF_EMPTY) : false;
242 bool DDF::isstring() const
244 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING) : false;
247 bool DDF::isint() const
249 return m_handle ? (m_handle->type==ddf_body_t::DDF_INT) : false;
252 bool DDF::isfloat() const
254 return m_handle ? (m_handle->type==ddf_body_t::DDF_FLOAT) : false;
257 bool DDF::isstruct() const
259 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRUCT) : false;
262 bool DDF::islist() const
264 return m_handle ? (m_handle->type==ddf_body_t::DDF_LIST) : false;
267 bool DDF::ispointer() const
269 return m_handle ? (m_handle->type==ddf_body_t::DDF_POINTER) : false;
272 const char* DDF::string() const
274 return isstring() ? m_handle->value.string : NULL;
277 long DDF::integer() const
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;
295 double DDF::floating() const
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;
313 void* DDF::pointer() const
315 return ispointer() ? m_handle->value.pointer : NULL;
318 size_t DDF::strlen() const
320 return ddf_strlen(string());
323 bool DDF::operator==(const char* s) const
325 if (string()==NULL || s==NULL)
326 return (string()==NULL && s==NULL);
328 return (::strcmp(string(),s)==0);
334 switch (m_handle->type) {
335 case ddf_body_t::DDF_STRING:
336 if (m_handle->value.string)
337 free(m_handle->value.string);
339 case ddf_body_t::DDF_LIST:
340 case ddf_body_t::DDF_STRUCT:
343 while (m_handle->value.children.first)
345 temp.m_handle=m_handle->value.children.first;
350 m_handle->type=ddf_body_t::DDF_EMPTY;
355 DDF& DDF::string(const char* val)
357 if (empty().m_handle) {
358 m_handle->value.string=ddf_strdup(val);
359 if (!m_handle->value.string && val && *val)
361 m_handle->type=ddf_body_t::DDF_STRING;
366 DDF& DDF::string(long val)
370 sprintf(buf,"%ld",val);
374 DDF& DDF::string(double val)
378 snprintf(buf,39,"%f",val);
382 DDF& DDF::integer(long val)
384 if (empty().m_handle) {
385 m_handle->value.integer=val;
386 m_handle->type=ddf_body_t::DDF_INT;
391 DDF& DDF::integer(const char* val)
393 if (empty().m_handle) {
394 m_handle->value.integer=(val ? atol(val) : 0);
395 m_handle->type=ddf_body_t::DDF_INT;
400 DDF& DDF::floating(double val)
402 if (empty().m_handle) {
403 m_handle->value.floating=val;
404 m_handle->type=ddf_body_t::DDF_FLOAT;
409 DDF& DDF::floating(const char* val)
411 if (empty().m_handle) {
412 m_handle->value.floating=(val ? atof(val) : 0);
413 m_handle->type=ddf_body_t::DDF_FLOAT;
418 DDF& DDF::structure()
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;
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;
442 DDF& DDF::pointer(void* val)
444 if (empty().m_handle) {
445 m_handle->value.pointer=val;
446 m_handle->type=ddf_body_t::DDF_POINTER;
451 DDF& DDF::add(DDF& child)
453 if ((!isstruct() && !islist()) || !child.m_handle)
456 if (m_handle==child.m_handle->parent)
462 getmember(child.name()).destroy();
466 if (!m_handle->value.children.first)
467 m_handle->value.children.first=child.m_handle;
469 m_handle->value.children.last->next=child.m_handle;
470 child.m_handle->prev=m_handle->value.children.last;
472 m_handle->value.children.last=child.m_handle;
473 child.m_handle->parent=m_handle;
474 m_handle->value.children.count++;
478 DDF& DDF::addbefore(DDF& child, DDF& before)
480 if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
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++;
496 DDF& DDF::addafter(DDF& child, DDF& after)
498 if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
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++;
514 void DDF::swap(DDF& arg)
516 ddf_body_t* temp=arg.m_handle;
517 arg.m_handle=m_handle;
523 if (!m_handle || !m_handle->parent)
527 m_handle->next->prev=m_handle->prev;
530 m_handle->prev->next=m_handle->next;
532 if (m_handle->parent->value.children.first==m_handle)
533 m_handle->parent->value.children.first=m_handle->next;
535 if (m_handle->parent->value.children.last==m_handle)
536 m_handle->parent->value.children.last=m_handle->prev;
538 if (m_handle->parent->value.children.current==m_handle)
539 m_handle->parent->value.children.current=m_handle->prev;
541 m_handle->parent->value.children.count--;
542 m_handle->parent=NULL;
548 DDF DDF::parent() const
552 p.m_handle=(m_handle ? m_handle->parent : NULL);
560 if (islist() || isstruct())
561 f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
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;
573 n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
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;
593 if (islist() || isstruct()) {
594 p.m_handle=m_handle->value.children.current;
596 m_handle->value.children.current=m_handle->value.children.current->prev;
601 DDF DDF::operator[](unsigned long index) const
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;
610 throw range_error("DDF object not a list with >=index+1 elements");
614 DDF DDF::addmember(const char* path)
616 char name[MAX_NAME_LEN+1];
617 const char* path_ptr=path;
619 if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
623 DDF new_member=getmember(name);
624 if (!new_member.m_handle) {
626 new_member=add(temp);
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();
644 DDF DDF::getmember(const char* path) const
646 char name[MAX_NAME_LEN+1];
647 const char* path_ptr=path;
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;
655 if (current.m_handle && ddf_strlen(path_ptr)>0)
656 current=current.getmember(path_ptr);
662 void ddf_print_indent(FILE* f, int indent)
664 for (; indent>0; indent--)
668 void DDF::dump(FILE* f, int indent) const
673 ddf_print_indent(f,indent);
675 switch (m_handle->type) {
677 case ddf_body_t::DDF_EMPTY:
680 fprintf(f," %s",m_handle->name);
683 case ddf_body_t::DDF_STRING:
685 fprintf(f,"char* %s = ",m_handle->name);
687 fprintf(f,"char* = ");
688 if (const char* chptr=m_handle->value.string) {
698 case ddf_body_t::DDF_INT:
700 fprintf(f,"long %s = ",m_handle->name);
702 fprintf(f,"long = ");
703 fprintf(f,"%ld",m_handle->value.integer);
706 case ddf_body_t::DDF_FLOAT:
708 fprintf(f,"double %s = ",m_handle->name);
710 fprintf(f,"double = ");
711 fprintf(f,"%.15f",m_handle->value.floating);
714 case ddf_body_t::DDF_STRUCT:
715 fprintf(f,"struct ");
717 fprintf(f,"%s ",m_handle->name);
719 if (m_handle->value.children.count) {
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;
728 ddf_print_indent(f,indent);
732 case ddf_body_t::DDF_LIST:
735 fprintf(f," %s",m_handle->name);
736 fprintf(f,"[%lu] {",m_handle->value.children.count);
737 if (m_handle->value.children.count) {
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;
746 ddf_print_indent(f,indent);
750 case ddf_body_t::DDF_POINTER:
752 fprintf(f,"void* %s = ",m_handle->name);
754 fprintf(f,"void* = ");
755 if (m_handle->value.pointer)
756 fprintf(f,"%p",m_handle->value.pointer);
762 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
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.
774 void xml_encode(ostream& os, const char* start)
776 while (start && *start) {
778 case '\'': os << "'"; break;
779 case '<': os << "<"; break;
780 case '>': os << ">"; break;
781 case '&': os << "&"; break;
782 default: os << *start;
788 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
793 case ddf_body_t::DDF_STRING:
795 if (name_attr && p->name) {
797 xml_encode(os,p->name);
800 if (p->value.string) {
802 xml_encode(os,p->value.string);
809 case ddf_body_t::DDF_INT:
811 if (name_attr && p->name) {
813 xml_encode(os,p->name);
816 os << '>' << p->value.integer << "</number>\n";
819 case ddf_body_t::DDF_FLOAT:
821 if (name_attr && p->name) {
823 xml_encode(os,p->name);
826 os << '>' << fixed << p->value.floating << dec << "</number>\n";
829 case ddf_body_t::DDF_STRUCT:
832 if (name_attr && p->name) {
834 xml_encode(os,p->name);
838 ddf_body_t* child=p->value.children.first;
841 xml_encode(os,child->name);
843 serialize(child,os,false);
851 case ddf_body_t::DDF_LIST:
853 os << "<array length='" << p->value.children.count << '\'';
854 if (name_attr && p->name) {
856 xml_encode(os,p->name);
860 ddf_body_t* child=p->value.children.first;
869 case ddf_body_t::DDF_EMPTY:
870 case ddf_body_t::DDF_POINTER:
873 if (name_attr && p->name) {
875 xml_encode(os,p->name);
886 // The stream insertion will work for any ostream-based object.
888 SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
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";
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.
900 static const char* g_DocType=
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\
924 // This function constructs a DDF object equivalent to the wddx data element rooted
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);
936 DDF deserialize(DOMElement* root, bool lowercase)
939 auto_ptr_char name_val(root->getAttribute(_name));
940 if (name_val.get() && *name_val.get()) {
942 for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
943 obj.name(name_val.get());
946 const XMLCh* tag=root->getTagName();
947 if (!XMLString::compareString(tag,_var)) {
948 root=XMLHelper::getFirstChildElement(root);
949 tag=(root ? root->getTagName() : &chNull);
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());
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());
966 obj.integer(val.get());
969 else if (!XMLString::compareString(tag,_array)) {
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);
978 else if (!XMLString::compareString(tag,_struct)) {
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);
991 SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
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()));
999 obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);