2 * Copyright 2001-2009 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 * C++ DDF abstraction for interpretive RPC
24 #include "remoting/ddf.h"
27 # define snprintf _snprintf
31 #include <xercesc/dom/DOM.hpp>
32 #include <xercesc/util/XMLUniDefs.hpp>
33 #include <xmltooling/XMLToolingConfig.h>
34 #include <xmltooling/util/ParserPool.h>
35 #include <xmltooling/util/URLEncoder.h>
36 #include <xmltooling/util/XMLHelper.h>
38 using namespace shibsp;
39 using namespace xmltooling;
40 using namespace xercesc;
43 // defensive string functions
45 size_t ddf_strlen(const char* s)
47 return s ? strlen(s) : 0;
50 char* ddf_strdup(const char* s)
52 return (s && *s) ? strdup(s) : 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)
66 if (*path==NULL || **path==0)
69 const char* temp=strchr(*path,'.');
71 strncpy(name,*path,MAX_NAME_LEN);
75 else if (temp>*path) {
76 strncpy(name,*path,temp-*path);
85 // body implementation
87 struct shibsp::ddf_body_t {
88 ddf_body_t() : name(NULL), parent(NULL), next(NULL), prev(NULL), type(DDF_EMPTY) {}
90 char* name; // name of node
91 ddf_body_t* parent; // parent node, if any
92 ddf_body_t* next; // next node, if any
93 ddf_body_t* prev; // previous node, if any
104 } type; // data type of node
117 } value; // value of node
120 // library implementation
122 DDF::DDF(const char* n)
124 m_handle=new(nothrow) ddf_body_t;
128 DDF::DDF(const char* n, const char* val, bool safe)
130 m_handle=new(nothrow) ddf_body_t;
132 string(const_cast<char*>(val), true, safe);
135 DDF::DDF(const char* n, long val)
137 m_handle=new(nothrow) ddf_body_t;
142 DDF::DDF(const char* n, double val)
144 m_handle=new(nothrow) ddf_body_t;
149 DDF::DDF(const char* n, void* val)
151 m_handle=new(nothrow) ddf_body_t;
158 remove().empty().name(NULL);
164 DDF DDF::copy() const
169 switch (m_handle->type) {
170 case ddf_body_t::DDF_EMPTY:
171 return DDF(m_handle->name);
172 case ddf_body_t::DDF_STRING:
173 case ddf_body_t::DDF_STRING_UNSAFE:
174 return DDF(m_handle->name,m_handle->value.string,(m_handle->type==ddf_body_t::DDF_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 || m_handle->type==ddf_body_t::DDF_STRING_UNSAFE) : 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 case ddf_body_t::DDF_STRING_UNSAFE:
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 case ddf_body_t::DDF_STRING_UNSAFE:
305 return m_handle->value.string ? atof(m_handle->value.string) : 0;
306 case ddf_body_t::DDF_STRUCT:
307 case ddf_body_t::DDF_LIST:
308 return m_handle->value.children.count;
314 void* DDF::pointer() const
316 return ispointer() ? m_handle->value.pointer : NULL;
319 size_t DDF::strlen() const
321 return ddf_strlen(string());
324 bool DDF::operator==(const char* s) const
326 if (string()==NULL || s==NULL)
327 return (string()==NULL && s==NULL);
329 return (::strcmp(string(),s)==0);
335 switch (m_handle->type) {
336 case ddf_body_t::DDF_STRING:
337 case ddf_body_t::DDF_STRING_UNSAFE:
338 if (m_handle->value.string)
339 free(m_handle->value.string);
341 case ddf_body_t::DDF_LIST:
342 case ddf_body_t::DDF_STRUCT:
345 while (m_handle->value.children.first)
347 temp.m_handle=m_handle->value.children.first;
352 m_handle->type=ddf_body_t::DDF_EMPTY;
357 DDF& DDF::string(char* val, bool copyit, bool safe)
359 if (empty().m_handle) {
360 m_handle->value.string = copyit ? ddf_strdup(val) : val;
361 if (!m_handle->value.string && val && *val)
363 m_handle->type=(safe ? ddf_body_t::DDF_STRING : ddf_body_t::DDF_STRING_UNSAFE);
368 DDF& DDF::string(long val)
372 sprintf(buf,"%ld",val);
376 DDF& DDF::string(double val)
380 snprintf(buf,39,"%f",val);
384 DDF& DDF::integer(long val)
386 if (empty().m_handle) {
387 m_handle->value.integer=val;
388 m_handle->type=ddf_body_t::DDF_INT;
393 DDF& DDF::integer(const char* val)
395 if (empty().m_handle) {
396 m_handle->value.integer=(val ? atol(val) : 0);
397 m_handle->type=ddf_body_t::DDF_INT;
402 DDF& DDF::floating(double val)
404 if (empty().m_handle) {
405 m_handle->value.floating=val;
406 m_handle->type=ddf_body_t::DDF_FLOAT;
411 DDF& DDF::floating(const char* val)
413 if (empty().m_handle) {
414 m_handle->value.floating=(val ? atof(val) : 0);
415 m_handle->type=ddf_body_t::DDF_FLOAT;
420 DDF& DDF::structure()
422 if (empty().m_handle) {
423 m_handle->type=ddf_body_t::DDF_STRUCT;
424 m_handle->value.children.first=NULL;
425 m_handle->value.children.last=NULL;
426 m_handle->value.children.current=NULL;
427 m_handle->value.children.count=0;
434 if (empty().m_handle) {
435 m_handle->type=ddf_body_t::DDF_LIST;
436 m_handle->value.children.first=NULL;
437 m_handle->value.children.last=NULL;
438 m_handle->value.children.current=NULL;
439 m_handle->value.children.count=0;
444 DDF& DDF::pointer(void* val)
446 if (empty().m_handle) {
447 m_handle->value.pointer=val;
448 m_handle->type=ddf_body_t::DDF_POINTER;
453 DDF& DDF::add(DDF& child)
455 if ((!isstruct() && !islist()) || !child.m_handle)
458 if (m_handle==child.m_handle->parent)
464 getmember(child.name()).destroy();
468 if (!m_handle->value.children.first)
469 m_handle->value.children.first=child.m_handle;
471 m_handle->value.children.last->next=child.m_handle;
472 child.m_handle->prev=m_handle->value.children.last;
474 m_handle->value.children.last=child.m_handle;
475 child.m_handle->parent=m_handle;
476 m_handle->value.children.count++;
480 DDF& DDF::addbefore(DDF& child, DDF& before)
482 if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
486 if (m_handle->value.children.first==before.m_handle)
487 m_handle->value.children.first=child.m_handle;
488 child.m_handle->prev=before.m_handle->prev;
489 if (child.m_handle->prev)
490 child.m_handle->prev->next=child.m_handle;
491 before.m_handle->prev=child.m_handle;
492 child.m_handle->next=before.m_handle;
493 child.m_handle->parent=m_handle;
494 m_handle->value.children.count++;
498 DDF& DDF::addafter(DDF& child, DDF& after)
500 if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
504 if (m_handle->value.children.last==after.m_handle)
505 m_handle->value.children.last=child.m_handle;
506 child.m_handle->next=after.m_handle->next;
507 if (child.m_handle->next)
508 child.m_handle->next->prev=child.m_handle;
509 after.m_handle->next=child.m_handle;
510 child.m_handle->prev=after.m_handle;
511 child.m_handle->parent=m_handle;
512 m_handle->value.children.count++;
516 void DDF::swap(DDF& arg)
518 ddf_body_t* temp=arg.m_handle;
519 arg.m_handle=m_handle;
525 if (!m_handle || !m_handle->parent)
529 m_handle->next->prev=m_handle->prev;
532 m_handle->prev->next=m_handle->next;
534 if (m_handle->parent->value.children.first==m_handle)
535 m_handle->parent->value.children.first=m_handle->next;
537 if (m_handle->parent->value.children.last==m_handle)
538 m_handle->parent->value.children.last=m_handle->prev;
540 if (m_handle->parent->value.children.current==m_handle)
541 m_handle->parent->value.children.current=m_handle->prev;
543 m_handle->parent->value.children.count--;
544 m_handle->parent=NULL;
550 DDF DDF::parent() const
554 p.m_handle=(m_handle ? m_handle->parent : NULL);
562 if (islist() || isstruct())
563 f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
571 if ((islist() || isstruct()) && m_handle->value.children.current!=m_handle->value.children.last) {
572 if (!m_handle->value.children.current)
573 n.m_handle=m_handle->value.children.current=m_handle->value.children.first;
575 n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
584 if ((islist() || isstruct()) && m_handle->value.children.last) {
585 m_handle->value.children.current=m_handle->value.children.last->prev;
586 l.m_handle=m_handle->value.children.last;
595 if (islist() || isstruct()) {
596 p.m_handle=m_handle->value.children.current;
598 m_handle->value.children.current=m_handle->value.children.current->prev;
603 DDF DDF::operator[](unsigned long index) const
607 if (islist() && index<m_handle->value.children.count) {
608 for (d.m_handle=m_handle->value.children.first; index; index--)
609 d.m_handle=d.m_handle->next;
612 throw range_error("DDF object not a list with >=index+1 elements");
616 DDF DDF::addmember(const char* path)
618 char name[MAX_NAME_LEN+1];
619 const char* path_ptr=path;
621 if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
625 DDF new_member=getmember(name);
626 if (!new_member.m_handle) {
628 new_member=add(temp);
631 if (new_member.m_handle) {
632 if (ddf_strlen(path_ptr)>0) {
633 DDF last_member=new_member.addmember(path_ptr);
634 if (!last_member.m_handle)
635 return new_member.destroy();
646 DDF DDF::getmember(const char* path) const
649 char name[MAX_NAME_LEN+1];
650 const char* path_ptr=path;
652 ddf_token(&path_ptr, name);
655 else if (*name == '[') {
656 unsigned long i = strtoul(name+1, NULL, 10);
657 if (islist() && i < m_handle->value.children.count)
658 current=operator[](i);
662 else if (isstruct()) {
663 current.m_handle = m_handle->value.children.first;
664 while (current.m_handle && strcmp(current.m_handle->name,name) != 0)
665 current.m_handle = current.m_handle->next;
668 current.m_handle = m_handle->value.children.first;
669 return current.getmember(path);
672 if (current.m_handle && path_ptr && *path_ptr)
673 current = current.getmember(path_ptr);
678 void ddf_print_indent(FILE* f, int indent)
680 for (; indent>0; indent--)
684 void DDF::dump(FILE* f, int indent) const
689 ddf_print_indent(f,indent);
691 switch (m_handle->type) {
693 case ddf_body_t::DDF_EMPTY:
696 fprintf(f," %s",m_handle->name);
699 case ddf_body_t::DDF_STRING:
700 case ddf_body_t::DDF_STRING_UNSAFE:
702 fprintf(f,"char* %s = ",m_handle->name);
704 fprintf(f,"char* = ");
705 if (const char* chptr=m_handle->value.string) {
715 case ddf_body_t::DDF_INT:
717 fprintf(f,"long %s = ",m_handle->name);
719 fprintf(f,"long = ");
720 fprintf(f,"%ld",m_handle->value.integer);
723 case ddf_body_t::DDF_FLOAT:
725 fprintf(f,"double %s = ",m_handle->name);
727 fprintf(f,"double = ");
728 fprintf(f,"%.15f",m_handle->value.floating);
731 case ddf_body_t::DDF_STRUCT:
732 fprintf(f,"struct ");
734 fprintf(f,"%s ",m_handle->name);
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_LIST:
752 fprintf(f," %s",m_handle->name);
753 fprintf(f,"[%lu] {",m_handle->value.children.count);
754 if (m_handle->value.children.count) {
757 child.m_handle=m_handle->value.children.first;
758 while (child.m_handle) {
759 child.dump(f,indent+2);
760 child.m_handle=child.m_handle->next;
763 ddf_print_indent(f,indent);
767 case ddf_body_t::DDF_POINTER:
769 fprintf(f,"void* %s = ",m_handle->name);
771 fprintf(f,"void* = ");
772 if (m_handle->value.pointer)
773 fprintf(f,"%p",m_handle->value.pointer);
779 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
787 // Serialization is fairly easy. We have to walk the DDF and hand-generate a
788 // wddxPacket XML fragment, with some simple extensions. We escape the four major
789 // special characters, which requires that we output strings one char at a time.
791 void xml_encode(ostream& os, const char* start)
794 while (start && *start) {
795 pos = strcspn(start, "\"<>&");
802 case '"': os << """; break;
803 case '<': os << "<"; break;
804 case '>': os << ">"; break;
805 case '&': os << "&"; break;
806 default: os << *start;
813 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
818 case ddf_body_t::DDF_STRING:
819 case ddf_body_t::DDF_STRING_UNSAFE:
821 if (name_attr && p->name) {
823 xml_encode(os,p->name);
826 if (p->value.string) {
827 if (p->type == ddf_body_t::DDF_STRING) {
829 xml_encode(os,p->value.string);
832 os << " unsafe=\"1\">";
833 xml_encode(os,XMLToolingConfig::getConfig().getURLEncoder()->encode(p->value.string).c_str());
841 case ddf_body_t::DDF_INT:
843 if (name_attr && p->name) {
845 xml_encode(os,p->name);
848 os << '>' << p->value.integer << "</number>";
851 case ddf_body_t::DDF_FLOAT:
853 if (name_attr && p->name) {
855 xml_encode(os,p->name);
858 os << '>' << fixed << p->value.floating << dec << "</number>";
861 case ddf_body_t::DDF_STRUCT:
864 if (name_attr && p->name) {
866 xml_encode(os,p->name);
870 ddf_body_t* child=p->value.children.first;
872 os << "<var name=\"";
873 xml_encode(os,child->name);
875 serialize(child,os,false);
883 case ddf_body_t::DDF_LIST:
885 os << "<array length=\"" << p->value.children.count << '"';
886 if (name_attr && p->name) {
888 xml_encode(os,p->name);
892 ddf_body_t* child=p->value.children.first;
901 case ddf_body_t::DDF_EMPTY:
902 case ddf_body_t::DDF_POINTER:
905 if (name_attr && p->name) {
907 xml_encode(os,p->name);
918 // The stream insertion will work for any ostream-based object.
920 SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
923 os << "<wddxPacket version=\"1.0\" lowercase=\"no\"><header/><data>";
924 serialize(obj.m_handle,os);
925 os << "</data></wddxPacket>";
929 // This is a DTD internal subset based on a compatible permutation of the WDDX spec, with the
930 // extension of a name attribute on all the typed elements, which DDF has, but WDDX does not.
933 static const char* g_DocType=
935 <!DOCTYPE wddxPacket [\n\
936 <!ELEMENT wddxPacket (header, data)>\n\
937 <!ATTLIST wddxPacket version CDATA #FIXED \"1.0\" lowercase (yes|no) \"yes\">\n\
938 <!ELEMENT header (comment?)>\n\
939 <!ELEMENT comment (#PCDATA)>\n\
940 <!ELEMENT data (null | number | string | array | struct)>\n\
941 <!ELEMENT null EMPTY>\n\
942 <!ATTLIST null name CDATA #IMPLIED type CDATA #IMPLIED>\n\
943 <!ELEMENT string (#PCDATA | char)*>\n\
944 <!ATTLIST string name CDATA #IMPLIED type CDATA #IMPLIED>\n\
945 <!ELEMENT char EMPTY>\n\
946 <!ATTLIST char code CDATA #REQUIRED>\n\
947 <!ELEMENT number (#PCDATA)>\n\
948 <!ATTLIST number name CDATA #IMPLIED type CDATA #IMPLIED>\n\
949 <!ELEMENT array (null | number | string | array | struct)*>\n\
950 <!ATTLIST array length CDATA #REQUIRED name CDATA #IMPLIED type CDATA #IMPLIED>\n\
951 <!ELEMENT struct (var*)>\n\
952 <!ATTLIST struct name CDATA #IMPLIED type CDATA #IMPLIED>\n\
953 <!ELEMENT var (null | number | string | array | struct)>\n\
954 <!ATTLIST var name CDATA #REQUIRED>\n\
958 // This function constructs a DDF object equivalent to the wddx data element rooted
961 static const XMLCh _no[] = UNICODE_LITERAL_2(n,o);
962 static const XMLCh _name[] = UNICODE_LITERAL_4(n,a,m,e);
963 static const XMLCh _var[] = UNICODE_LITERAL_3(v,a,r);
964 static const XMLCh _string[] = UNICODE_LITERAL_6(s,t,r,i,n,g);
965 static const XMLCh _number[] = UNICODE_LITERAL_6(n,u,m,b,e,r);
966 static const XMLCh _array[] = UNICODE_LITERAL_5(a,r,r,a,y);
967 static const XMLCh _struct[] = UNICODE_LITERAL_6(s,t,r,u,c,t);
968 static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e);
969 static const XMLCh _unsafe[] = UNICODE_LITERAL_6(u,n,s,a,f,e);
971 DDF deserialize(DOMElement* root, bool lowercase)
974 auto_ptr_char name_val(root->getAttributeNS(NULL, _name));
975 if (name_val.get() && *name_val.get()) {
977 for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
978 obj.name(name_val.get());
981 const XMLCh* tag=root->getTagName();
982 if (XMLString::equals(tag,_var)) {
983 root=XMLHelper::getFirstChildElement(root);
984 tag=(root ? root->getTagName() : &chNull);
987 if (XMLString::equals(tag,_string)) {
988 DOMNode* child=root->getFirstChild();
989 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
990 const XMLCh* unsafe = root->getAttributeNS(NULL, _unsafe);
991 if (unsafe && *unsafe==chDigit_1) {
992 // If it's unsafe, it's not UTF-8 data, so we have to convert to ASCII and decode it.
993 char* encoded = XMLString::transcode(child->getNodeValue());
994 XMLToolingConfig::getConfig().getURLEncoder()->decode(encoded);
995 obj.string(encoded, true, false); // re-copy into free-able buffer, plus mark unsafe
996 XMLString::release(&encoded);
999 char* val = toUTF8(child->getNodeValue(), true); // use malloc
1001 obj.string(val, false); // don't re-copy the string
1005 else if (XMLString::equals(tag,_number)) {
1006 DOMNode* child=root->getFirstChild();
1007 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
1008 auto_ptr_char val(child->getNodeValue());
1009 if (val.get() && strchr(val.get(),'.'))
1010 obj.floating(val.get());
1012 obj.integer(val.get());
1015 else if (XMLString::equals(tag,_array)) {
1017 DOMNodeList* children=root->getChildNodes();
1018 for (unsigned int i=0; children && i<children->getLength(); i++)
1019 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
1020 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
1024 else if (XMLString::equals(tag,_struct)) {
1026 DOMNodeList* children=root->getChildNodes();
1027 for (unsigned int i=0; children && i<children->getLength(); i++)
1028 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
1029 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
1037 SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
1039 // Parse the input stream into a DOM tree and construct the equivalent DDF.
1040 DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(is);
1041 XercesJanitor<DOMDocument> docj(doc);
1042 const XMLCh* lowercase=doc->getDocumentElement()->getAttribute(_lowercase);
1043 DOMElement* first=XMLHelper::getFirstChildElement(XMLHelper::getLastChildElement(doc->getDocumentElement()));
1045 obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);