ctkLDAPExpr.cpp

Go to the documentation of this file.
00001 /*=============================================================================
00002 
00003 Library: CTK
00004 
00005 Copyright (c) 2010 German Cancer Research Center,
00006 Division of Medical and Biological Informatics
00007 
00008 Licensed under the Apache License, Version 2.0 (the "License");
00009 you may not use this file except in compliance with the License.
00010 You may obtain a copy of the License at
00011 
00012 http://www.apache.org/licenses/LICENSE-2.0
00013 
00014 Unless required by applicable law or agreed to in writing, software
00015 distributed under the License is distributed on an "AS IS" BASIS,
00016 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00017 See the License for the specific language governing permissions and
00018 limitations under the License.
00019 
00020 =============================================================================*/
00021 
00022 #include "ctkLDAPExpr.h"
00023 #include <QSet>
00024 #include <QVariant>
00025 
00026 const QChar ctkLDAPExpr::WILDCARD = 65535;
00027 const QString ctkLDAPExpr::WILDCARD_QString = QString( WILDCARD );
00028 const QString ctkLDAPExpr::NULLQ      = "Null query";
00029 const QString ctkLDAPExpr::GARBAGE   = "Trailing garbage";
00030 const QString ctkLDAPExpr::EOS       = "Unexpected end of query";
00031 const QString ctkLDAPExpr::MALFORMED = "Malformed query";
00032 const QString ctkLDAPExpr::OPERATOR  = "Undefined operator";
00033 
00034 ctkLDAPExpr::ctkLDAPExpr( const QString &filter ) throw (ctkInvalidSyntaxException)
00035 {
00036   ParseState ps(filter);
00037   try {
00038 
00039     ctkLDAPExpr expr = parseExpr(ps);
00040  
00041     if (ps.rest().trimmed ().length() != 0)
00042       ps.error(GARBAGE + " '" + ps.rest() + "'");
00043 
00044     d = new ctkLDAPExprData( *expr.d.data() );
00045 
00046   } catch ( std::out_of_range e) {
00047     ps.error(EOS);
00048   }
00049 }
00050 
00051 ctkLDAPExpr::ctkLDAPExpr( int op, const QList<ctkLDAPExpr> &args )
00052 {
00053   d = new ctkLDAPExprData( op, args );
00054 }
00055 
00056 ctkLDAPExpr::ctkLDAPExpr( int op, const QString &attrName, const QString &attrValue )
00057 {
00058   d = new ctkLDAPExprData( op, attrName, attrValue );
00059 }
00060 
00061 ctkLDAPExpr::ctkLDAPExpr( const ctkLDAPExpr& other )
00062 {
00063   d = new ctkLDAPExprData( *other.d.data() );
00064 }
00065 
00066 QSet<QString> ctkLDAPExpr::getMatchedObjectClasses() const
00067 {
00068   QSet<QString> objClasses;
00069   if (d->m_operator == EQ) 
00070   {
00071     if (d->m_attrName.compare(PluginConstants::OBJECTCLASS, Qt::CaseInsensitive) &&
00072       d->m_attrValue.indexOf(WILDCARD) < 0) 
00073     {
00074       objClasses.insert( d->m_attrValue );
00075     }
00076   }
00077   else if (d->m_operator == AND) 
00078   {
00079     for (int i = 0; i < d->m_args.size( ); i++) {
00080       QSet<QString> r = d->m_args[i].getMatchedObjectClasses();
00081       if ( !r.empty() ) {
00082         if (objClasses.empty()) {
00083           objClasses = r;
00084         } else {
00085           // if AND op and classes in several operands,
00086           // then only the intersection is possible.
00087           objClasses = r;
00088         }
00089       }
00090     }
00091   } else if (d->m_operator == OR) {
00092     for (int i = 0; i < d->m_args.length( ); i++) {
00093       QSet<QString> r = d->m_args[i].getMatchedObjectClasses();
00094       if ( !r.empty() ) {
00095         objClasses += r;
00096       } else {
00097         objClasses.clear();
00098         break;
00099       }
00100     }
00101   }
00102   return objClasses;
00103 }
00104 
00105 bool ctkLDAPExpr::isSimple( 
00106   const QList<QString> &keywords, 
00107   QHash<int, QList<QString> > &cache, 
00108   bool matchCase ) const
00109 {
00110   if (d->m_operator == EQ) {
00111     int index;
00112     if ((index = keywords.indexOf(matchCase ? d->m_attrName : d->m_attrName.toLower())) >= 0 &&
00113       d->m_attrValue.indexOf(WILDCARD) < 0) {
00114         cache[index] += d->m_attrValue;
00115         return true;
00116     }
00117   } else if (d->m_operator == OR) {
00118     for (int i = 0; i < d->m_args.size( ); i++) {
00119       if (!d->m_args[i].isSimple(keywords, cache, matchCase))
00120         return false;
00121     }
00122     return true;
00123   }
00124   return false;
00125 }
00126 
00127 bool ctkLDAPExpr::query( const QString &filter, const ctkDictionary &pd ) throw (ctkInvalidSyntaxException)
00128 {
00129   return ctkLDAPExpr(filter).evaluate(pd, false);
00130 }
00131 
00132 bool ctkLDAPExpr::evaluate( const ctkDictionary &p, bool matchCase ) const
00133 {
00134   if ((d->m_operator & SIMPLE) != 0) {
00135     return compare(p[ matchCase ? d->m_attrName : d->m_attrName.toLower() ],
00136       d->m_operator, d->m_attrValue);
00137   } else { // (d->m_operator & COMPLEX) != 0
00138     switch (d->m_operator) {
00139     case AND:
00140       for (int i = 0; i < d->m_args.length( ); i++) {
00141         if (!d->m_args[i].evaluate(p, matchCase))
00142           return false;
00143       }
00144       return true;
00145     case OR:
00146       for (int i = 0; i < d->m_args.length( ); i++) {
00147         if (d->m_args[i].evaluate(p, matchCase))
00148           return true;
00149       }
00150       return false;
00151     case NOT:
00152       return !d->m_args[0].evaluate(p, matchCase);
00153     default:
00154       return false; // Cannot happen
00155     }
00156   }
00157 }
00158 
00159 bool ctkLDAPExpr::compare( const QVariant &obj, int op, const QString &s ) const
00160 {
00161   if (obj.isNull())
00162     return false;
00163   if (op == EQ && s == WILDCARD_QString )
00164     return true;
00165   try {
00166     if ( obj.canConvert<QString>( ) ) {
00167       return compareQString(obj.toString(), op, s);
00168     } else if (obj.canConvert<char>( ) ) {
00169       return compareQString(obj.toString(), op, s);
00170     } else if (obj.canConvert<bool>( ) ) {
00171       if (op==LE || op==GE)
00172         return false;
00173       if ( obj.toBool() ) {
00174         return s.compare("true", Qt::CaseInsensitive);
00175       } else {
00176         return s.compare("false", Qt::CaseInsensitive);
00177       }
00178     } 
00179     else if ( obj.canConvert<Byte>( ) || obj.canConvert<int>( ) ) 
00180     {
00181       switch(op) {
00182       case LE:
00183         return obj.toInt() <= s.toInt();
00184       case GE:
00185         return obj.toInt() >= s.toInt();
00186       default: /*APPROX and EQ*/
00187         return s.toInt( ) == obj.toInt();
00188       }
00189     } else if ( obj.canConvert<float>( ) ) {
00190       switch(op) {
00191       case LE:
00192         return obj.toFloat() <= s.toFloat();
00193       case GE:
00194         return obj.toFloat() >= s.toFloat();
00195       default: /*APPROX and EQ*/
00196         return s.toFloat() == obj.toFloat();
00197       }
00198     } else if (obj.canConvert<double>()) {
00199       switch(op) {
00200       case LE:
00201         return obj.toDouble() <= s.toDouble();
00202       case GE:
00203         return obj.toDouble() >= s.toDouble();
00204       default: /*APPROX and EQ*/
00205         return s.toDouble( ) == obj.toDouble( );
00206       }
00207     } else if (obj.canConvert<qlonglong>( )) {
00208       switch(op) {
00209       case LE:
00210         return obj.toLongLong() <= s.toLongLong( );
00211       case GE:
00212         return obj.toLongLong() >= s.toLongLong( );
00213       default: /*APPROX and EQ*/
00214         return obj.toLongLong() == s.toLongLong( );
00215       }
00216     } 
00217     else if (obj.canConvert< QList<QVariant> >()) {
00218       QList<QVariant> list = obj.toList();
00219       QList<QVariant>::Iterator it;
00220       for (it=list.begin(); it != list.end( ); it++)
00221          if (compare(*it, op, s))
00222            return true;
00223     } 
00224   } catch (...) {
00225     // This might happen if a QString-to-datatype conversion fails
00226     // Just consider it a false match and ignore the exception
00227   }
00228   return false;
00229 }
00230 
00231 bool ctkLDAPExpr::compareQString( const QString &s1, int op, const QString &s2 )
00232 {
00233   switch(op) {
00234   case LE:
00235     return s1.compare(s2) <= 0;
00236   case GE:
00237     return s1.compare(s2) >= 0;
00238   case EQ:
00239     return patSubstr(s1,s2);
00240   case APPROX:
00241     return fixupQString(s2) == fixupQString(s1);
00242   default:
00243     return false;
00244   }
00245 }
00246 
00247 const QString ctkLDAPExpr::fixupQString( const QString &s )
00248 {
00249   QString sb;
00250   int len = s.length();
00251   for(int i=0; i<len; i++) {
00252     QChar c = s.at(i);
00253     if (!c.isSpace()) {
00254       if (c.isUpper())
00255         c = c.toLower();
00256       sb.append(c);
00257     }
00258   }
00259   return sb;
00260 }
00261 
00262 
00263 bool ctkLDAPExpr::patSubstr( const QString &s, int si, const QString &pat, int pi )
00264 {
00265   if (pat.size( )-pi == 0)
00266     return s.size( )-si == 0;
00267   if (QChar( pat[pi] ) == WILDCARD ) {
00268     pi++;
00269     for (;;) {
00270       if (patSubstr( s, si, pat, pi))
00271         return true;
00272       if (s.size( )-si == 0)
00273         return false;
00274       si++;
00275     }
00276   } else {
00277     if (s.size( )-si==0){
00278       return false;
00279     }
00280     if(s[si]!=pat[pi]){
00281       return false;
00282     }
00283     return patSubstr( s, ++si, pat, ++pi);
00284   }
00285 }
00286 
00287 bool ctkLDAPExpr::patSubstr( const QString &s, const QString &pat )
00288 {
00289   return s.isNull() ? false : patSubstr(s,0,pat,0);
00290 }
00291 
00292 ctkLDAPExpr ctkLDAPExpr::parseExpr( ParseState &ps ) throw (ctkInvalidSyntaxException)
00293 {
00294   ps.skipWhite();
00295   if (!ps.prefix("("))
00296     ps.error(MALFORMED);
00297 
00298   int op;
00299   ps.skipWhite();
00300   QChar c = ps.peek();
00301   if ( c == '&') {
00302     op = AND;
00303   }else if ( c == '|' ){
00304     op = OR; 
00305   } else if ( c == '!' ) {
00306     op = NOT;
00307   } else {
00308     return parseSimple(ps);
00309   }
00310   ps.skip(1); // Ignore the d->m_operator
00311   QList<ctkLDAPExpr> v;
00312   do {
00313     v.append(parseExpr(ps));
00314     ps.skipWhite();
00315   } while (ps.peek() == '(');
00316   int n = v.size();
00317   if (!ps.prefix(")") || n == 0 || (op == NOT && n > 1))
00318     ps.error(MALFORMED);
00319 
00320   return ctkLDAPExpr(op, v);
00321 }
00322 
00323 ctkLDAPExpr ctkLDAPExpr::parseSimple( ParseState &ps ) throw (ctkInvalidSyntaxException)
00324 {
00325   QString attrName = ps.getAttributeName();
00326   if (attrName.isNull())
00327     ps.error(MALFORMED);
00328   int op = 0;
00329   if (ps.prefix("="))
00330     op = EQ;
00331   else if (ps.prefix("<="))
00332     op = LE;
00333   else if(ps.prefix(">="))
00334     op = GE;
00335   else if(ps.prefix("~="))
00336     op = APPROX;
00337   else {
00338     //      System.out.println("undef op='" + ps.peek() + "'");
00339     ps.error(OPERATOR); // Does not return
00340   }
00341   QString attrValue = ps.getAttributeValue();
00342   if (!ps.prefix(")"))
00343     ps.error(MALFORMED);
00344   return ctkLDAPExpr(op, attrName, attrValue);
00345 }
00346 
00347 const QString ctkLDAPExpr::toQString() const
00348 {
00349   QString res;
00350   res.append("(");
00351   if ((d->m_operator & SIMPLE) != 0) {
00352     res.append(d->m_attrName);
00353     switch (d->m_operator) {
00354     case EQ:
00355       res.append("=");
00356       break;
00357     case LE:
00358       res.append("<=");
00359       break;
00360     case GE:
00361       res.append(">=");
00362       break;
00363     case APPROX:
00364       res.append("~=");
00365       break;
00366     }
00367     for (int i = 0; i < d->m_attrValue.length(); i++) {
00368       QChar c = d->m_attrValue.at(i);
00369       if (c ==  '(' || c == ')' || c == '*' || c == '\\') {
00370         res.append('\\');
00371       } else if (c == WILDCARD) {
00372         c = '*';
00373       }
00374       res.append(c);
00375     }
00376   } else {
00377     switch (d->m_operator) {
00378     case AND:
00379       res.append("&");
00380       break;
00381     case OR:
00382       res.append("|");
00383       break;
00384     case NOT:
00385       res.append("!");
00386       break;
00387     }
00388     for (int i = 0; i < d->m_args.length( ); i++) {
00389       res.append(d->m_args[i].toQString());
00390     }
00391   }
00392   res.append(")");
00393   return res;
00394 }
00395 
00396 ctkLDAPExpr::ParseState::ParseState( const QString &str ) throw (ctkInvalidSyntaxException)
00397 {
00398   m_str = str;
00399   if (m_str.length() == 0)
00400     error(NULLQ);
00401   m_pos = 0;
00402 }
00403 
00404 bool ctkLDAPExpr::ParseState::prefix( const QString &pre )
00405 {
00406   if (!m_str.startsWith(pre.mid(m_pos)))
00407     return false;
00408   m_pos += pre.length();
00409   return true;
00410 }
00411 
00412 QChar ctkLDAPExpr::ParseState::peek()
00413 {
00414   if ( m_pos >= m_str.size() )
00415   {
00416     throw std::out_of_range( "LDAPExpr" );
00417   }
00418   return m_str.at(m_pos);
00419 }
00420 
00421 void ctkLDAPExpr::ParseState::skip( int n )
00422 {
00423   m_pos += n;
00424 }
00425 
00426 const QString ctkLDAPExpr::ParseState::rest()
00427 {
00428   return m_str.mid(m_pos);
00429 }
00430 
00431 void ctkLDAPExpr::ParseState::skipWhite()
00432 {
00433   while ( peek( ).isSpace( ) ) {
00434     m_pos++;
00435   }
00436 }
00437 
00438 const QString ctkLDAPExpr::ParseState::getAttributeName()
00439 {
00440   int start = m_pos;
00441   int n = -1;
00442   for(;; m_pos++) {
00443     QChar c = peek( );
00444     if (c == '(' || c == ')' ||
00445       c == '<' || c == '>' ||
00446       c == '=' || c == '~') {
00447         break;
00448     } else if ( !c.isSpace( ) ) {
00449       n = m_pos - start + 1;
00450     }
00451   }
00452   if (n == -1) {
00453     return QString::Null( );
00454   }
00455   return m_str.mid(start, n);
00456 }
00457 
00458 const QString ctkLDAPExpr::ParseState::getAttributeValue()
00459 {
00460   QString sb;
00461   bool exit = false;
00462   while( !exit ) {
00463     QChar c = peek( );
00464     switch(c.toLatin1()) {
00465     case '(':
00466     case ')':
00467     exit = true;
00468       break;
00469     case '*':
00470       sb.append(WILDCARD);
00471       break;
00472     case '\\':
00473       sb.append(m_str.at(++m_pos));
00474       break;
00475     default:
00476       sb.append(c);
00477       break;
00478     }
00479     if ( !exit )
00480     {
00481       m_pos++;
00482     }
00483   }
00484   return sb;
00485 }
00486 
00487 void ctkLDAPExpr::ParseState::error( const QString &m ) throw (ctkInvalidSyntaxException)
00488 {
00489   QString error = m + ": " + (m_str.isNull() ? "" : m_str.mid(m_pos) );
00490   throw ctkInvalidSyntaxException( error.toStdString() );
00491 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated on 21 May 2010 for CTK by  doxygen 1.6.1