Ver código fonte

ENH: Added LDAPExpr

Xavi Planes 15 anos atrás
pai
commit
087b5b7bd4

+ 4 - 3
Libs/PluginFramework/CMakeLists.txt

@@ -73,6 +73,7 @@ SET(KIT_SRCS
   ctkPluginStorage.cpp
   ctkVersion.cpp
   ctkVersionRange.cpp
+  ctkLDAPExpr.cpp
 
   # EventBus sources
   EventBus/ctkEvent.cpp
@@ -119,6 +120,6 @@ ctkMacroBuildLib(
   )
 
 # Testing
-#IF(BUILD_TESTING)
-#  ADD_SUBDIRECTORY(Testing)
-#ENDIF(BUILD_TESTING)
+IF(BUILD_TESTING)
+  ADD_SUBDIRECTORY(Testing)
+ENDIF(BUILD_TESTING)

+ 1 - 0
Libs/PluginFramework/Testing/CMakeLists.txt

@@ -0,0 +1 @@
+ADD_SUBDIRECTORY(Cpp)

+ 30 - 0
Libs/PluginFramework/Testing/Cpp/CMakeLists.txt

@@ -0,0 +1,30 @@
+SET(KIT ${PROJECT_NAME})
+
+CREATE_TEST_SOURCELIST(Tests ${KIT}CppTests.cpp
+  ctkLDAPExpreTest.cpp
+  )
+
+SET (TestsToRun ${Tests})
+REMOVE (TestsToRun ${KIT}CppTests.cpp)
+
+SET(LIBRARY_NAME ${PROJECT_NAME})
+
+ADD_EXECUTABLE(${KIT}CppTests ${Tests})
+TARGET_LINK_LIBRARIES(${KIT}CppTests ${LIBRARY_NAME} ${CTK_BASE_LIBRARIES})
+
+SET( KIT_TESTS ${CPP_TEST_PATH}/${KIT}CppTests)
+IF(WIN32)
+  SET(KIT_TESTS ${CPP_TEST_PATH}/${CMAKE_BUILD_TYPE}/${KIT}CppTests)
+ENDIF(WIN32)
+
+MACRO( SIMPLE_TEST  TESTNAME )
+  ADD_TEST( ${TESTNAME} ${KIT_TESTS} ${TESTNAME} )
+  SET_PROPERTY(TEST ${TESTNAME} PROPERTY LABELS ${PROJECT_NAME})
+ENDMACRO( SIMPLE_TEST  )
+
+#
+# Add Tests
+#
+
+SIMPLE_TEST( ctkLDAPExpreTest )
+

+ 144 - 0
Libs/PluginFramework/Testing/Cpp/ctkLDAPExpreTest.cpp

@@ -0,0 +1,144 @@
+/*=========================================================================
+
+  Library:   CTK
+ 
+  Copyright (c) 2010  Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ 
+=========================================================================*/
+
+// CTK includes
+#include "ctkLDAPExpr.h"
+
+#include <iostream>
+
+#include <QVariant>
+
+
+int TestParsing( );
+int TestEvaluate( );
+
+//-----------------------------------------------------------------------------
+int ctkLDAPExpreTest(int argc, char * argv [] )
+{
+  Q_UNUSED(argc);
+  Q_UNUSED(argv);  
+
+  if ( TestParsing( ) != EXIT_SUCCESS )
+  {
+    return EXIT_FAILURE;
+  }
+
+  if ( TestEvaluate( ) != EXIT_SUCCESS )
+  {
+    return EXIT_FAILURE;
+  }
+
+  return EXIT_SUCCESS;
+}
+
+int TestParsing( ) 
+{
+
+  // WELL FORMED Expr
+  try
+  {
+    ctkLDAPExpr ldap( "(cn=Babs Jensen)" );
+    ldap = ctkLDAPExpr( "(!(cn=Tim Howes))" );
+    ldap = ctkLDAPExpr( "(&(" + PluginConstants::OBJECTCLASS + "=Person)(|(sn=Jensen)(cn=Babs J*)))" );
+    ldap = ctkLDAPExpr( "(o=univ*of*mich*)" );
+    ldap = ctkLDAPExpr( "(cn=Babs Jensen)" );
+  }
+  catch ( ctkInvalidSyntaxException &e )
+  {
+    std::cerr << e.what() << std::endl;
+    return EXIT_FAILURE;
+  }
+
+
+  // MALFORMED Expre
+  try
+  {
+    ctkLDAPExpr ldap( "cn=Babs Jensen)" );
+    return EXIT_FAILURE;
+  }
+  catch ( ctkInvalidSyntaxException &e )
+  {
+    // Nothing to do
+    int i = 0;
+  }
+
+  return EXIT_SUCCESS;
+}
+
+
+int TestEvaluate( )
+{
+  // EVALUATE
+  try
+  {
+    ctkLDAPExpr ldap( "(cn=Babs Jensen)" );
+    ctkDictionary dict;
+    bool eval = false;
+
+    // Several values
+    dict.insert( "cn", "Babs Jensen" );
+    dict.insert( "unused", "Jansen" );
+    eval = ldap.evaluate( dict, true );
+    if ( !eval )
+    {
+      return EXIT_FAILURE;
+    }
+
+    // WILDCARD
+    ldap = ctkLDAPExpr( "(cn=Babs *)" );
+    dict.clear();
+    dict.insert( "cn", "Babs Jensen" );
+    eval = ldap.evaluate( dict, true );
+    if ( !eval )
+    {
+      return EXIT_FAILURE;
+    }
+
+    // NOT FOUND
+    ldap = ctkLDAPExpr( "(cn=Babs *)" );
+    dict.clear();
+    dict.insert( "unused", "New" );
+    eval = ldap.evaluate( dict, true );
+    if ( eval )
+    {
+      return EXIT_FAILURE;
+    }
+
+    // QList with integer values
+    ldap = ctkLDAPExpr( "  ( |(cn=Babs *)(sn=1) )" );
+    dict.clear();
+    QList<QVariant> list;
+    list.append( "Babs Jensen" );
+    list.append( "1" );
+    dict.insert( "sn", list );
+    eval = ldap.evaluate( dict, true );
+    if ( !eval )
+    {
+      return EXIT_FAILURE;
+    }
+  }
+  catch ( ctkInvalidSyntaxException &e )
+  {
+    std::cerr << e.what() << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  return EXIT_SUCCESS;
+}

+ 491 - 0
Libs/PluginFramework/ctkLDAPExpr.cpp

@@ -0,0 +1,491 @@
+/*=============================================================================
+
+Library: CTK
+
+Copyright (c) 2010 German Cancer Research Center,
+Division of Medical and Biological Informatics
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+=============================================================================*/
+
+#include "ctkLDAPExpr.h"
+#include <QSet>
+#include <QVariant>
+
+const QChar ctkLDAPExpr::WILDCARD = 65535;
+const QString ctkLDAPExpr::WILDCARD_QString = QString( WILDCARD );
+const QString ctkLDAPExpr::NULLQ      = "Null query";
+const QString ctkLDAPExpr::GARBAGE   = "Trailing garbage";
+const QString ctkLDAPExpr::EOS       = "Unexpected end of query";
+const QString ctkLDAPExpr::MALFORMED = "Malformed query";
+const QString ctkLDAPExpr::OPERATOR  = "Undefined operator";
+
+ctkLDAPExpr::ctkLDAPExpr( const QString &filter ) throw (ctkInvalidSyntaxException)
+{
+  ParseState ps(filter);
+  try {
+
+    ctkLDAPExpr expr = parseExpr(ps);
+ 
+    if (ps.rest().trimmed ().length() != 0)
+      ps.error(GARBAGE + " '" + ps.rest() + "'");
+
+    d = new ctkLDAPExprData( *expr.d.data() );
+
+  } catch ( std::out_of_range e) {
+    ps.error(EOS);
+  }
+}
+
+ctkLDAPExpr::ctkLDAPExpr( int op, const QList<ctkLDAPExpr> &args )
+{
+  d = new ctkLDAPExprData( op, args );
+}
+
+ctkLDAPExpr::ctkLDAPExpr( int op, const QString &attrName, const QString &attrValue )
+{
+  d = new ctkLDAPExprData( op, attrName, attrValue );
+}
+
+ctkLDAPExpr::ctkLDAPExpr( const ctkLDAPExpr& other )
+{
+  d = new ctkLDAPExprData( *other.d.data() );
+}
+
+QSet<QString> ctkLDAPExpr::getMatchedObjectClasses() const
+{
+  QSet<QString> objClasses;
+  if (d->m_operator == EQ) 
+  {
+    if (d->m_attrName.compare(PluginConstants::OBJECTCLASS, Qt::CaseInsensitive) &&
+      d->m_attrValue.indexOf(WILDCARD) < 0) 
+    {
+      objClasses.insert( d->m_attrValue );
+    }
+  }
+  else if (d->m_operator == AND) 
+  {
+    for (int i = 0; i < d->m_args.size( ); i++) {
+      QSet<QString> r = d->m_args[i].getMatchedObjectClasses();
+      if ( !r.empty() ) {
+        if (objClasses.empty()) {
+          objClasses = r;
+        } else {
+          // if AND op and classes in several operands,
+          // then only the intersection is possible.
+          objClasses = r;
+        }
+      }
+    }
+  } else if (d->m_operator == OR) {
+    for (int i = 0; i < d->m_args.length( ); i++) {
+      QSet<QString> r = d->m_args[i].getMatchedObjectClasses();
+      if ( !r.empty() ) {
+        objClasses += r;
+      } else {
+        objClasses.clear();
+        break;
+      }
+    }
+  }
+  return objClasses;
+}
+
+bool ctkLDAPExpr::isSimple( 
+  const QList<QString> &keywords, 
+  QHash<int, QList<QString> > &cache, 
+  bool matchCase ) const
+{
+  if (d->m_operator == EQ) {
+    int index;
+    if ((index = keywords.indexOf(matchCase ? d->m_attrName : d->m_attrName.toLower())) >= 0 &&
+      d->m_attrValue.indexOf(WILDCARD) < 0) {
+        cache[index] += d->m_attrValue;
+        return true;
+    }
+  } else if (d->m_operator == OR) {
+    for (int i = 0; i < d->m_args.size( ); i++) {
+      if (!d->m_args[i].isSimple(keywords, cache, matchCase))
+        return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+bool ctkLDAPExpr::query( const QString &filter, const ctkDictionary &pd ) throw (ctkInvalidSyntaxException)
+{
+  return ctkLDAPExpr(filter).evaluate(pd, false);
+}
+
+bool ctkLDAPExpr::evaluate( const ctkDictionary &p, bool matchCase ) const
+{
+  if ((d->m_operator & SIMPLE) != 0) {
+    return compare(p[ matchCase ? d->m_attrName : d->m_attrName.toLower() ],
+      d->m_operator, d->m_attrValue);
+  } else { // (d->m_operator & COMPLEX) != 0
+    switch (d->m_operator) {
+    case AND:
+      for (int i = 0; i < d->m_args.length( ); i++) {
+        if (!d->m_args[i].evaluate(p, matchCase))
+          return false;
+      }
+      return true;
+    case OR:
+      for (int i = 0; i < d->m_args.length( ); i++) {
+        if (d->m_args[i].evaluate(p, matchCase))
+          return true;
+      }
+      return false;
+    case NOT:
+      return !d->m_args[0].evaluate(p, matchCase);
+    default:
+      return false; // Cannot happen
+    }
+  }
+}
+
+bool ctkLDAPExpr::compare( const QVariant &obj, int op, const QString &s ) const
+{
+  if (obj.isNull())
+    return false;
+  if (op == EQ && s == WILDCARD_QString )
+    return true;
+  try {
+    if ( obj.canConvert<QString>( ) ) {
+      return compareQString(obj.toString(), op, s);
+    } else if (obj.canConvert<char>( ) ) {
+      return compareQString(obj.toString(), op, s);
+    } else if (obj.canConvert<bool>( ) ) {
+      if (op==LE || op==GE)
+        return false;
+      if ( obj.toBool() ) {
+        return s.compare("true", Qt::CaseInsensitive);
+      } else {
+        return s.compare("false", Qt::CaseInsensitive);
+      }
+    } 
+    else if ( obj.canConvert<Byte>( ) || obj.canConvert<int>( ) ) 
+    {
+      switch(op) {
+      case LE:
+        return obj.toInt() <= s.toInt();
+      case GE:
+        return obj.toInt() >= s.toInt();
+      default: /*APPROX and EQ*/
+        return s.toInt( ) == obj.toInt();
+      }
+    } else if ( obj.canConvert<float>( ) ) {
+      switch(op) {
+      case LE:
+        return obj.toFloat() <= s.toFloat();
+      case GE:
+        return obj.toFloat() >= s.toFloat();
+      default: /*APPROX and EQ*/
+        return s.toFloat() == obj.toFloat();
+      }
+    } else if (obj.canConvert<double>()) {
+      switch(op) {
+      case LE:
+        return obj.toDouble() <= s.toDouble();
+      case GE:
+        return obj.toDouble() >= s.toDouble();
+      default: /*APPROX and EQ*/
+        return s.toDouble( ) == obj.toDouble( );
+      }
+    } else if (obj.canConvert<qlonglong>( )) {
+      switch(op) {
+      case LE:
+        return obj.toLongLong() <= s.toLongLong( );
+      case GE:
+        return obj.toLongLong() >= s.toLongLong( );
+      default: /*APPROX and EQ*/
+        return obj.toLongLong() == s.toLongLong( );
+      }
+    } 
+    else if (obj.canConvert< QList<QVariant> >()) {
+      QList<QVariant> list = obj.toList();
+      QList<QVariant>::Iterator it;
+      for (it=list.begin(); it != list.end( ); it++)
+         if (compare(*it, op, s))
+           return true;
+    } 
+  } catch (...) {
+    // This might happen if a QString-to-datatype conversion fails
+    // Just consider it a false match and ignore the exception
+  }
+  return false;
+}
+
+bool ctkLDAPExpr::compareQString( const QString &s1, int op, const QString &s2 )
+{
+  switch(op) {
+  case LE:
+    return s1.compare(s2) <= 0;
+  case GE:
+    return s1.compare(s2) >= 0;
+  case EQ:
+    return patSubstr(s1,s2);
+  case APPROX:
+    return fixupQString(s2) == fixupQString(s1);
+  default:
+    return false;
+  }
+}
+
+const QString ctkLDAPExpr::fixupQString( const QString &s )
+{
+  QString sb;
+  int len = s.length();
+  for(int i=0; i<len; i++) {
+    QChar c = s.at(i);
+    if (!c.isSpace()) {
+      if (c.isUpper())
+        c = c.toLower();
+      sb.append(c);
+    }
+  }
+  return sb;
+}
+
+
+bool ctkLDAPExpr::patSubstr( const QString &s, int si, const QString &pat, int pi )
+{
+  if (pat.size( )-pi == 0)
+    return s.size( )-si == 0;
+  if (QChar( pat[pi] ) == WILDCARD ) {
+    pi++;
+    for (;;) {
+      if (patSubstr( s, si, pat, pi))
+        return true;
+      if (s.size( )-si == 0)
+        return false;
+      si++;
+    }
+  } else {
+    if (s.size( )-si==0){
+      return false;
+    }
+    if(s[si]!=pat[pi]){
+      return false;
+    }
+    return patSubstr( s, ++si, pat, ++pi);
+  }
+}
+
+bool ctkLDAPExpr::patSubstr( const QString &s, const QString &pat )
+{
+  return s.isNull() ? false : patSubstr(s,0,pat,0);
+}
+
+ctkLDAPExpr ctkLDAPExpr::parseExpr( ParseState &ps ) throw (ctkInvalidSyntaxException)
+{
+  ps.skipWhite();
+  if (!ps.prefix("("))
+    ps.error(MALFORMED);
+
+  int op;
+  ps.skipWhite();
+  QChar c = ps.peek();
+  if ( c == '&') {
+    op = AND;
+  }else if ( c == '|' ){
+    op = OR; 
+  } else if ( c == '!' ) {
+    op = NOT;
+  } else {
+    return parseSimple(ps);
+  }
+  ps.skip(1); // Ignore the d->m_operator
+  QList<ctkLDAPExpr> v;
+  do {
+    v.append(parseExpr(ps));
+    ps.skipWhite();
+  } while (ps.peek() == '(');
+  int n = v.size();
+  if (!ps.prefix(")") || n == 0 || (op == NOT && n > 1))
+    ps.error(MALFORMED);
+
+  return ctkLDAPExpr(op, v);
+}
+
+ctkLDAPExpr ctkLDAPExpr::parseSimple( ParseState &ps ) throw (ctkInvalidSyntaxException)
+{
+  QString attrName = ps.getAttributeName();
+  if (attrName.isNull())
+    ps.error(MALFORMED);
+  int op = 0;
+  if (ps.prefix("="))
+    op = EQ;
+  else if (ps.prefix("<="))
+    op = LE;
+  else if(ps.prefix(">="))
+    op = GE;
+  else if(ps.prefix("~="))
+    op = APPROX;
+  else {
+    //      System.out.println("undef op='" + ps.peek() + "'");
+    ps.error(OPERATOR); // Does not return
+  }
+  QString attrValue = ps.getAttributeValue();
+  if (!ps.prefix(")"))
+    ps.error(MALFORMED);
+  return ctkLDAPExpr(op, attrName, attrValue);
+}
+
+const QString ctkLDAPExpr::toQString() const
+{
+  QString res;
+  res.append("(");
+  if ((d->m_operator & SIMPLE) != 0) {
+    res.append(d->m_attrName);
+    switch (d->m_operator) {
+    case EQ:
+      res.append("=");
+      break;
+    case LE:
+      res.append("<=");
+      break;
+    case GE:
+      res.append(">=");
+      break;
+    case APPROX:
+      res.append("~=");
+      break;
+    }
+    for (int i = 0; i < d->m_attrValue.length(); i++) {
+      QChar c = d->m_attrValue.at(i);
+      if (c ==  '(' || c == ')' || c == '*' || c == '\\') {
+        res.append('\\');
+      } else if (c == WILDCARD) {
+        c = '*';
+      }
+      res.append(c);
+    }
+  } else {
+    switch (d->m_operator) {
+    case AND:
+      res.append("&");
+      break;
+    case OR:
+      res.append("|");
+      break;
+    case NOT:
+      res.append("!");
+      break;
+    }
+    for (int i = 0; i < d->m_args.length( ); i++) {
+      res.append(d->m_args[i].toQString());
+    }
+  }
+  res.append(")");
+  return res;
+}
+
+ctkLDAPExpr::ParseState::ParseState( const QString &str ) throw (ctkInvalidSyntaxException)
+{
+  m_str = str;
+  if (m_str.length() == 0)
+    error(NULLQ);
+  m_pos = 0;
+}
+
+bool ctkLDAPExpr::ParseState::prefix( const QString &pre )
+{
+  if (!m_str.startsWith(pre.mid(m_pos)))
+    return false;
+  m_pos += pre.length();
+  return true;
+}
+
+QChar ctkLDAPExpr::ParseState::peek()
+{
+  if ( m_pos >= m_str.size() )
+  {
+    throw std::out_of_range( "LDAPExpr" );
+  }
+  return m_str.at(m_pos);
+}
+
+void ctkLDAPExpr::ParseState::skip( int n )
+{
+  m_pos += n;
+}
+
+const QString ctkLDAPExpr::ParseState::rest()
+{
+  return m_str.mid(m_pos);
+}
+
+void ctkLDAPExpr::ParseState::skipWhite()
+{
+  while ( peek( ).isSpace( ) ) {
+    m_pos++;
+  }
+}
+
+const QString ctkLDAPExpr::ParseState::getAttributeName()
+{
+  int start = m_pos;
+  int n = -1;
+  for(;; m_pos++) {
+    QChar c = peek( );
+    if (c == '(' || c == ')' ||
+      c == '<' || c == '>' ||
+      c == '=' || c == '~') {
+        break;
+    } else if ( !c.isSpace( ) ) {
+      n = m_pos - start + 1;
+    }
+  }
+  if (n == -1) {
+    return QString::Null( );
+  }
+  return m_str.mid(start, n);
+}
+
+const QString ctkLDAPExpr::ParseState::getAttributeValue()
+{
+  QString sb;
+  bool exit = false;
+  while( !exit ) {
+    QChar c = peek( );
+    switch(c.toLatin1()) {
+    case '(':
+    case ')':
+    exit = true;
+      break;
+    case '*':
+      sb.append(WILDCARD);
+      break;
+    case '\\':
+      sb.append(m_str.at(++m_pos));
+      break;
+    default:
+      sb.append(c);
+      break;
+    }
+    if ( !exit )
+    {
+      m_pos++;
+    }
+  }
+  return sb;
+}
+
+void ctkLDAPExpr::ParseState::error( const QString &m ) throw (ctkInvalidSyntaxException)
+{
+  QString error = m + ": " + (m_str.isNull() ? "" : m_str.mid(m_pos) );
+  throw ctkInvalidSyntaxException( error.toStdString() );
+}

+ 241 - 0
Libs/PluginFramework/ctkLDAPExpr.h

@@ -0,0 +1,241 @@
+/*=============================================================================
+
+Library: CTK
+
+Copyright (c) 2010 German Cancer Research Center,
+Division of Medical and Biological Informatics
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+=============================================================================*/
+
+#include "ctkPluginConstants.h"
+#include <exception>
+
+#include <QString>
+#include <QHash>
+#include <QSharedDataPointer>
+
+#include <stdexcept>
+
+
+
+typedef std::invalid_argument ctkInvalidSyntaxException;
+typedef QHash< QString, QVariant > ctkDictionary;
+class ctkLDAPExprData;
+
+/**
+\brief LDAP Expression
+\date 19 May 2010
+\author Xavi Planes
+\ingroup ctkPluginFramework
+*/
+class CTK_PLUGINFW_EXPORT ctkLDAPExpr {
+
+public:
+  const static int AND     =  0;
+  const static int OR      =  1;
+  const static int NOT     =  2;
+  const static int EQ      =  4;
+  const static int LE      =  8;
+  const static int GE      = 16;
+  const static int APPROX  = 32;
+  const static int COMPLEX = AND | OR | NOT;
+  const static int SIMPLE  = EQ | LE | GE | APPROX;
+  typedef char Byte;
+
+public:
+  //!
+  ctkLDAPExpr(const QString &filter) throw ( ctkInvalidSyntaxException );
+
+  //!
+  ctkLDAPExpr(const ctkLDAPExpr& other);
+
+  /**
+   * Get object class set matched by this LDAP expression. This will not work
+   * with wildcards and NOT expressions. If a set can not be determined return null.
+   *
+   * @return std::set of classes matched, otherwise <code>null</code>.
+   */
+  QSet<QString> getMatchedObjectClasses() const;
+
+
+  /**
+   * Checks if this LDAP expression is "simple". The definition of
+   * a simple filter is:
+   * <ul>
+   *  <li><code>(<it>name</it>=<it>value</it>)</code> is simple if
+   *      <it>name</it> is a member of the provided <code>keywords</code>,
+   *      and <it>value</it> does not contain a wildcard character;</li>
+   *  <li><code>(| EXPR+ )</code> is simple if all <code>EXPR</code>
+   *      expressions are simple;</li>
+   *  <li>No other expressions are simple.</li>
+   * </ul>
+   * If the filter is found to be simple, the <code>cache</code> is
+   * filled with mappings from the provided keywords to lists
+   * of attribute values. The keyword-value-pairs are the ones that
+   * satisfy this expression, for the given keywords.
+   *
+   * @param keywords The keywords to look for.
+   * @param cache An array (indexed by the keyword indexes) of lists to
+   * fill in with values saturating this expression.
+   * @return <code>true</code> if this expression is simple,
+   * <code>false</code> otherwise.
+   */
+  bool isSimple(
+    const QList<QString> &keywords, 
+    QHash<int, QList<QString> > &cache, 
+    bool matchCase) const;
+
+  //! 
+  static bool query(const QString &filter, const ctkDictionary &pd)
+    throw (ctkInvalidSyntaxException);
+
+  //! Evaluate this LDAP filter.
+  bool evaluate(const ctkDictionary &p, bool matchCase) const;
+
+  //! 
+  const QString toQString() const;
+
+
+private:
+  //!
+  bool compare(const QVariant &obj, int op, const QString &s) const;
+
+  //! 
+  static bool compareQString(const QString &s1, int op, const QString &s2);
+
+  //! 
+  const static QString fixupQString(const QString &s);
+
+  //! 
+  static bool patSubstr(const QString &s, const QString &pat);
+
+  //!
+  static bool patSubstr(const QString &s, int si, const QString &pat, int pi);
+
+  //! Contains the current parser position and parsing utility methods.
+  class ParseState {
+    int m_pos;
+    QString m_str;
+
+  public:
+    ParseState(const QString &str) throw (ctkInvalidSyntaxException);
+
+    //! Move m_pos to remove the prefix \a pre
+    bool prefix(const QString &pre);
+
+    /** Peek a char at m_pos
+    \note If index out of bounds, throw exception
+    */
+    QChar peek();
+
+    //! Increment m_pos by n
+    void skip(int n);
+
+    //! return string from m_pos until the end
+    const QString rest();
+
+    //! Move m_pos until there's no spaces
+    void skipWhite();
+
+    //! Get string until special chars. Move m_pos
+    const QString getAttributeName();
+
+    //! Get string and convert * to WILDCARD
+    const QString getAttributeValue();
+
+    //! Throw InvalidSyntaxException exception
+    void error(const QString &m) throw (ctkInvalidSyntaxException);
+
+  };
+
+  //!
+  static ctkLDAPExpr parseExpr(ParseState &ps)
+    throw (ctkInvalidSyntaxException);
+
+  //!
+  static ctkLDAPExpr parseSimple(ParseState &ps)
+    throw (ctkInvalidSyntaxException);
+
+private:
+  //!
+  ctkLDAPExpr(int op, const QList<ctkLDAPExpr> &args);
+
+  //!
+  ctkLDAPExpr(int op, const QString &attrName, const QString &attrValue);
+
+
+private:
+  const static QChar WILDCARD; // = 65535;
+  const static QString WILDCARD_QString;// = QString( WILDCARD );
+
+  const static QString NULLQ;//      = "Null query";
+  const static QString GARBAGE;//   = "Trailing garbage";
+  const static QString EOS;//       = "Unexpected end of query";
+  const static QString MALFORMED;// = "Malformed query";
+  const static QString OPERATOR;//  = "Undefined m_operator";
+
+private:
+
+  //! Shared pointer
+  QSharedDataPointer<ctkLDAPExprData> d;
+
+};
+
+
+/**
+\brief LDAP Expression Data
+\date 19 May 2010
+\author Xavi Planes
+\ingroup ctkPluginFramework
+*/
+class ctkLDAPExprData : public QSharedData
+{
+public:
+
+  ctkLDAPExprData( int op, QList<ctkLDAPExpr> args )
+  {
+    m_operator = op;
+    m_args = args;
+    m_attrName = QString::Null( );
+    m_attrValue = QString::Null( );
+  }
+
+  ctkLDAPExprData( int op, QString attrName, QString attrValue )
+  {
+    m_operator = op;
+    m_args.clear();
+    m_attrName = attrName;
+    m_attrValue = attrValue;
+  }
+
+  ctkLDAPExprData( const ctkLDAPExprData& other ) : QSharedData(other)
+  {
+    m_operator = other.m_operator;
+    m_args = other.m_args;
+    m_attrName = other.m_attrName;
+    m_attrValue = other.m_attrValue;
+  }
+
+  //!
+  int m_operator;
+  //!
+  QList<ctkLDAPExpr> m_args;
+  //!
+  QString m_attrName;
+  //!
+  QString m_attrValue;
+};
+
+

+ 1 - 33
Libs/PluginFramework/ctkServices.cpp

@@ -31,43 +31,11 @@
 #include "ctkPluginConstants.h"
 #include "ctkServiceRegistrationPrivate.h"
 #include "ctkQtServiceRegistration_p.h"
+#include "ctkLDAPExpr.h"
 
 
   using namespace QtMobility;
 
-  //TODO: this is just a mock class. ctkWait for the real thing
-  class ctkLDAPExpr
-  {
-
-  public:
-
-    ctkLDAPExpr(const QString& filter)
-    {
-
-    }
-
-    /**
-     * Get object class set matched by this LDAP expression. This will not work
-     * with wildcards and NOT expressions. If a set can not be determined return null.
-     *
-     * @return Set of classes matched, otherwise <code>null</code>.
-     */
-    QSet<QString> getMatchedObjectClasses() const
-    {
-      QSet<QString> objClasses;
-      return objClasses;
-    }
-
-
-    /**
-     * Evaluate this LDAP filter.
-     */
-    bool evaluate(const ServiceProperties& p, bool matchCase)
-    {
-      return true;
-    }
-
-  };
 
 
   struct ServiceRegistrationComparator