2 * Copyright 2001-2007 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/XMLHelper.h>
37 using namespace shibsp;
38 using namespace xmltooling;
39 using namespace xercesc;
42 // defensive string functions
44 size_t ddf_strlen(const char* s)
46 return s ? strlen(s) : 0;
49 char* ddf_strdup(const char* s)
51 return (s && *s) ? strdup(s) : NULL;
54 #define MAX_NAME_LEN 255
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)
64 const char* temp=NULL;
67 if (*path==NULL || **path=='\0')
70 temp=strchr(*path,'.');
78 strncpy(name,*path,temp-*path);
79 name[temp-*path]='\0';
87 // body implementation
89 struct shibsp::ddf_body_t {
90 ddf_body_t() : name(NULL), parent(NULL), next(NULL), prev(NULL), type(DDF_EMPTY) {}
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
105 } type; // data type of node
118 } value; // value of node
121 // library implementation
123 DDF::DDF(const char* n)
125 m_handle=new(nothrow) ddf_body_t;
129 DDF::DDF(const char* n, const char* val)
131 m_handle=new(nothrow) ddf_body_t;
136 DDF::DDF(const char* n, long val)
138 m_handle=new(nothrow) ddf_body_t;
143 DDF::DDF(const char* n, double val)
145 m_handle=new(nothrow) ddf_body_t;
150 DDF::DDF(const char* n, void* val)
152 m_handle=new(nothrow) ddf_body_t;
159 remove().empty().name(NULL);
165 DDF DDF::copy() const
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:
184 DDF copy(m_handle->name), temp;
185 if (m_handle->type==ddf_body_t::DDF_STRUCT)
189 ddf_body_t* child=m_handle->value.children.first;
192 DDF temp2=temp.copy();
194 if (copy.m_handle==NULL)
196 if (m_handle->value.children.current==child)
197 copy.m_handle->value.children.current=copy.m_handle->value.children.last;
206 const char* DDF::name() const
208 return (m_handle) ? m_handle->name : NULL;
211 DDF& DDF::name(const char* name)
213 char trunc_name[MAX_NAME_LEN+1]="";
217 free(m_handle->name);
219 strncpy(trunc_name,name,MAX_NAME_LEN);
220 trunc_name[MAX_NAME_LEN]='\0';
221 m_handle->name=ddf_strdup(trunc_name);
231 bool DDF::isnull() const
233 return m_handle ? false : true;
236 bool DDF::isempty() const
238 return m_handle ? (m_handle->type==ddf_body_t::DDF_EMPTY) : false;
241 bool DDF::isstring() const
243 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING) : false;
246 bool DDF::isint() const
248 return m_handle ? (m_handle->type==ddf_body_t::DDF_INT) : false;
251 bool DDF::isfloat() const
253 return m_handle ? (m_handle->type==ddf_body_t::DDF_FLOAT) : false;
256 bool DDF::isstruct() const
258 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRUCT) : false;
261 bool DDF::islist() const
263 return m_handle ? (m_handle->type==ddf_body_t::DDF_LIST) : false;
266 bool DDF::ispointer() const
268 return m_handle ? (m_handle->type==ddf_body_t::DDF_POINTER) : false;
271 const char* DDF::string() const
273 return isstring() ? m_handle->value.string : NULL;
276 long DDF::integer() const
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;
294 double DDF::floating() const
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;
312 void* DDF::pointer() const
314 return ispointer() ? m_handle->value.pointer : NULL;
317 size_t DDF::strlen() const
319 return ddf_strlen(string());
322 bool DDF::operator==(const char* s) const
324 if (string()==NULL || s==NULL)
325 return (string()==NULL && s==NULL);
327 return (::strcmp(string(),s)==0);
333 switch (m_handle->type) {
334 case ddf_body_t::DDF_STRING:
335 if (m_handle->value.string)
336 free(m_handle->value.string);
338 case ddf_body_t::DDF_LIST:
339 case ddf_body_t::DDF_STRUCT:
342 while (m_handle->value.children.first)
344 temp.m_handle=m_handle->value.children.first;
349 m_handle->type=ddf_body_t::DDF_EMPTY;
354 DDF& DDF::string(const char* val)
356 if (empty().m_handle) {
357 m_handle->value.string=ddf_strdup(val);
358 if (!m_handle->value.string && val && *val)
360 m_handle->type=ddf_body_t::DDF_STRING;
365 DDF& DDF::string(long val)
369 sprintf(buf,"%ld",val);
373 DDF& DDF::string(double val)
377 snprintf(buf,39,"%f",val);
381 DDF& DDF::integer(long val)
383 if (empty().m_handle) {
384 m_handle->value.integer=val;
385 m_handle->type=ddf_body_t::DDF_INT;
390 DDF& DDF::integer(const char* val)
392 if (empty().m_handle) {
393 m_handle->value.integer=(val ? atol(val) : 0);
394 m_handle->type=ddf_body_t::DDF_INT;
399 DDF& DDF::floating(double val)
401 if (empty().m_handle) {
402 m_handle->value.floating=val;
403 m_handle->type=ddf_body_t::DDF_FLOAT;
408 DDF& DDF::floating(const char* val)
410 if (empty().m_handle) {
411 m_handle->value.floating=(val ? atof(val) : 0);
412 m_handle->type=ddf_body_t::DDF_FLOAT;
417 DDF& DDF::structure()
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;
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;
441 DDF& DDF::pointer(void* val)
443 if (empty().m_handle) {
444 m_handle->value.pointer=val;
445 m_handle->type=ddf_body_t::DDF_POINTER;
450 DDF& DDF::add(DDF& child)
452 if ((!isstruct() && !islist()) || !child.m_handle)
455 if (m_handle==child.m_handle->parent)
461 getmember(child.name()).destroy();
465 if (!m_handle->value.children.first)
466 m_handle->value.children.first=child.m_handle;
468 m_handle->value.children.last->next=child.m_handle;
469 child.m_handle->prev=m_handle->value.children.last;
471 m_handle->value.children.last=child.m_handle;
472 child.m_handle->parent=m_handle;
473 m_handle->value.children.count++;
477 DDF& DDF::addbefore(DDF& child, DDF& before)
479 if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
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++;
495 DDF& DDF::addafter(DDF& child, DDF& after)
497 if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
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++;
513 void DDF::swap(DDF& arg)
515 ddf_body_t* temp=arg.m_handle;
516 arg.m_handle=m_handle;
522 if (!m_handle || !m_handle->parent)
526 m_handle->next->prev=m_handle->prev;
529 m_handle->prev->next=m_handle->next;
531 if (m_handle->parent->value.children.first==m_handle)
532 m_handle->parent->value.children.first=m_handle->next;
534 if (m_handle->parent->value.children.last==m_handle)
535 m_handle->parent->value.children.last=m_handle->prev;
537 if (m_handle->parent->value.children.current==m_handle)
538 m_handle->parent->value.children.current=m_handle->prev;
540 m_handle->parent->value.children.count--;
541 m_handle->parent=NULL;
547 DDF DDF::parent() const
551 p.m_handle=(m_handle ? m_handle->parent : NULL);
559 if (islist() || isstruct())
560 f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
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;
572 n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
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;
592 if (islist() || isstruct()) {
593 p.m_handle=m_handle->value.children.current;
595 m_handle->value.children.current=m_handle->value.children.current->prev;
600 DDF DDF::operator[](unsigned long index) const
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;
609 throw range_error("DDF object not a list with >=index+1 elements");
613 DDF DDF::addmember(const char* path)
615 char name[MAX_NAME_LEN+1];
616 const char* path_ptr=path;
618 if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
622 DDF new_member=getmember(name);
623 if (!new_member.m_handle) {
625 new_member=add(temp);
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();
643 DDF DDF::getmember(const char* path) const
645 char name[MAX_NAME_LEN+1];
646 const char* path_ptr=path;
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;
654 if (current.m_handle && ddf_strlen(path_ptr)>0)
655 current=current.getmember(path_ptr);
661 void ddf_print_indent(FILE* f, int indent)
663 for (; indent>0; indent--)
667 void DDF::dump(FILE* f, int indent) const
672 ddf_print_indent(f,indent);
674 switch (m_handle->type) {
676 case ddf_body_t::DDF_EMPTY:
679 fprintf(f," %s",m_handle->name);
682 case ddf_body_t::DDF_STRING:
684 fprintf(f,"char* %s = ",m_handle->name);
686 fprintf(f,"char* = ");
687 if (const char* chptr=m_handle->value.string) {
697 case ddf_body_t::DDF_INT:
699 fprintf(f,"long %s = ",m_handle->name);
701 fprintf(f,"long = ");
702 fprintf(f,"%ld",m_handle->value.integer);
705 case ddf_body_t::DDF_FLOAT:
707 fprintf(f,"double %s = ",m_handle->name);
709 fprintf(f,"double = ");
710 fprintf(f,"%.15f",m_handle->value.floating);
713 case ddf_body_t::DDF_STRUCT:
714 fprintf(f,"struct ");
716 fprintf(f,"%s ",m_handle->name);
718 if (m_handle->value.children.count) {
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;
727 ddf_print_indent(f,indent);
731 case ddf_body_t::DDF_LIST:
734 fprintf(f," %s",m_handle->name);
735 fprintf(f,"[%lu] {",m_handle->value.children.count);
736 if (m_handle->value.children.count) {
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;
745 ddf_print_indent(f,indent);
749 case ddf_body_t::DDF_POINTER:
751 fprintf(f,"void* %s = ",m_handle->name);
753 fprintf(f,"void* = ");
754 if (m_handle->value.pointer)
755 fprintf(f,"%p",m_handle->value.pointer);
761 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
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.
773 void xml_encode(ostream& os, const char* start)
775 while (start && *start) {
777 case '\'': os << "'"; break;
778 case '<': os << "<"; break;
779 case '>': os << ">"; break;
780 case '&': os << "&"; break;
781 default: os << *start;
787 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
792 case ddf_body_t::DDF_STRING:
794 if (name_attr && p->name) {
796 xml_encode(os,p->name);
799 if (p->value.string) {
801 xml_encode(os,p->value.string);
808 case ddf_body_t::DDF_INT:
810 if (name_attr && p->name) {
812 xml_encode(os,p->name);
815 os << '>' << p->value.integer << "</number>\n";
818 case ddf_body_t::DDF_FLOAT:
820 if (name_attr && p->name) {
822 xml_encode(os,p->name);
825 os << '>' << fixed << p->value.floating << dec << "</number>\n";
828 case ddf_body_t::DDF_STRUCT:
831 if (name_attr && p->name) {
833 xml_encode(os,p->name);
837 ddf_body_t* child=p->value.children.first;
840 xml_encode(os,child->name);
842 serialize(child,os,false);
850 case ddf_body_t::DDF_LIST:
852 os << "<array length='" << p->value.children.count << '\'';
853 if (name_attr && p->name) {
855 xml_encode(os,p->name);
859 ddf_body_t* child=p->value.children.first;
868 case ddf_body_t::DDF_EMPTY:
869 case ddf_body_t::DDF_POINTER:
872 if (name_attr && p->name) {
874 xml_encode(os,p->name);
885 // The stream insertion will work for any ostream-based object.
887 SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
890 os << "<wddxPacket version=\"1.0\" lowercase=\"no\">\n<header/>\n<data>\n";
891 serialize(obj.m_handle,os);
892 os << "</data>\n</wddxPacket>\n";
896 // This is a DTD internal subset based on a compatible permutation of the WDDX spec, with the
897 // extension of a name attribute on all the typed elements, which DDF has, but WDDX does not.
899 static const char* g_DocType=
901 <!DOCTYPE wddxPacket [\n\
902 <!ELEMENT wddxPacket (header, data)>\n\
903 <!ATTLIST wddxPacket version CDATA #FIXED \"1.0\" lowercase (yes|no) \"yes\">\n\
904 <!ELEMENT header (comment?)>\n\
905 <!ELEMENT comment (#PCDATA)>\n\
906 <!ELEMENT data (null | number | string | array | struct)>\n\
907 <!ELEMENT null EMPTY>\n\
908 <!ATTLIST null name CDATA #IMPLIED type CDATA #IMPLIED>\n\
909 <!ELEMENT string (#PCDATA | char)*>\n\
910 <!ATTLIST string name CDATA #IMPLIED type CDATA #IMPLIED>\n\
911 <!ELEMENT char EMPTY>\n\
912 <!ATTLIST char code CDATA #REQUIRED>\n\
913 <!ELEMENT number (#PCDATA)>\n\
914 <!ATTLIST number name CDATA #IMPLIED type CDATA #IMPLIED>\n\
915 <!ELEMENT array (null | number | string | array | struct)*>\n\
916 <!ATTLIST array length CDATA #REQUIRED name CDATA #IMPLIED type CDATA #IMPLIED>\n\
917 <!ELEMENT struct (var*)>\n\
918 <!ATTLIST struct name CDATA #IMPLIED type CDATA #IMPLIED>\n\
919 <!ELEMENT var (null | number | string | array | struct)>\n\
920 <!ATTLIST var name CDATA #REQUIRED>\n\
923 // This function constructs a DDF object equivalent to the wddx data element rooted
926 static const XMLCh _no[] = UNICODE_LITERAL_2(n,o);
927 static const XMLCh _name[] = UNICODE_LITERAL_4(n,a,m,e);
928 static const XMLCh _var[] = UNICODE_LITERAL_3(v,a,r);
929 static const XMLCh _string[] = UNICODE_LITERAL_6(s,t,r,i,n,g);
930 static const XMLCh _number[] = UNICODE_LITERAL_6(n,u,m,b,e,r);
931 static const XMLCh _array[] = UNICODE_LITERAL_5(a,r,r,a,y);
932 static const XMLCh _struct[] = UNICODE_LITERAL_6(s,t,r,u,c,t);
933 static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e);
935 DDF deserialize(DOMElement* root, bool lowercase)
938 auto_ptr_char name_val(root->getAttribute(_name));
939 if (name_val.get() && *name_val.get()) {
941 for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
942 obj.name(name_val.get());
945 const XMLCh* tag=root->getTagName();
946 if (!XMLString::compareString(tag,_var)) {
947 root=XMLHelper::getFirstChildElement(root);
948 tag=(root ? root->getTagName() : &chNull);
951 if (!XMLString::compareString(tag,_string)) {
952 DOMNode* child=root->getFirstChild();
953 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
954 auto_ptr_char val(child->getNodeValue());
955 obj.string(val.get());
958 else if (!XMLString::compareString(tag,_number)) {
959 DOMNode* child=root->getFirstChild();
960 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
961 auto_ptr_char val(child->getNodeValue());
962 if (val.get() && strchr(val.get(),'.'))
963 obj.floating(val.get());
965 obj.integer(val.get());
968 else if (!XMLString::compareString(tag,_array)) {
970 DOMNodeList* children=root->getChildNodes();
971 for (unsigned int i=0; children && i<children->getLength(); i++)
972 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
973 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
977 else if (!XMLString::compareString(tag,_struct)) {
979 DOMNodeList* children=root->getChildNodes();
980 for (unsigned int i=0; children && i<children->getLength(); i++)
981 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
982 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
990 SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
992 // Parse the input stream into a DOM tree and construct the equivalent DDF.
993 DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(is);
994 XercesJanitor<DOMDocument> docj(doc);
995 const XMLCh* lowercase=doc->getDocumentElement()->getAttribute(_lowercase);
996 DOMElement* first=XMLHelper::getFirstChildElement(XMLHelper::getLastChildElement(doc->getDocumentElement()));
998 obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);