ctkLDAPExpr.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*=============================================================================
  2. Library: CTK
  3. Copyright (c) 2010 German Cancer Research Center,
  4. Division of Medical and Biological Informatics
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. =============================================================================*/
  15. #include "ctkLDAPExpr.h"
  16. #include <QSet>
  17. #include <QVariant>
  18. const QChar ctkLDAPExpr::WILDCARD = 65535;
  19. const QString ctkLDAPExpr::WILDCARD_QString = QString( WILDCARD );
  20. const QString ctkLDAPExpr::NULLQ = "Null query";
  21. const QString ctkLDAPExpr::GARBAGE = "Trailing garbage";
  22. const QString ctkLDAPExpr::EOS = "Unexpected end of query";
  23. const QString ctkLDAPExpr::MALFORMED = "Malformed query";
  24. const QString ctkLDAPExpr::OPERATOR = "Undefined operator";
  25. ctkLDAPExpr::ctkLDAPExpr( const QString &filter ) throw (ctkInvalidSyntaxException)
  26. {
  27. ParseState ps(filter);
  28. try {
  29. ctkLDAPExpr expr = parseExpr(ps);
  30. if (ps.rest().trimmed ().length() != 0)
  31. ps.error(GARBAGE + " '" + ps.rest() + "'");
  32. d = new ctkLDAPExprData( *expr.d.data() );
  33. } catch ( std::out_of_range e) {
  34. ps.error(EOS);
  35. }
  36. }
  37. ctkLDAPExpr::ctkLDAPExpr( int op, const QList<ctkLDAPExpr> &args )
  38. {
  39. d = new ctkLDAPExprData( op, args );
  40. }
  41. ctkLDAPExpr::ctkLDAPExpr( int op, const QString &attrName, const QString &attrValue )
  42. {
  43. d = new ctkLDAPExprData( op, attrName, attrValue );
  44. }
  45. ctkLDAPExpr::ctkLDAPExpr( const ctkLDAPExpr& other )
  46. {
  47. d = new ctkLDAPExprData( *other.d.data() );
  48. }
  49. QSet<QString> ctkLDAPExpr::getMatchedObjectClasses() const
  50. {
  51. QSet<QString> objClasses;
  52. if (d->m_operator == EQ)
  53. {
  54. if (d->m_attrName.compare(PluginConstants::OBJECTCLASS, Qt::CaseInsensitive) &&
  55. d->m_attrValue.indexOf(WILDCARD) < 0)
  56. {
  57. objClasses.insert( d->m_attrValue );
  58. }
  59. }
  60. else if (d->m_operator == AND)
  61. {
  62. for (int i = 0; i < d->m_args.size( ); i++) {
  63. QSet<QString> r = d->m_args[i].getMatchedObjectClasses();
  64. if ( !r.empty() ) {
  65. if (objClasses.empty()) {
  66. objClasses = r;
  67. } else {
  68. // if AND op and classes in several operands,
  69. // then only the intersection is possible.
  70. objClasses = r;
  71. }
  72. }
  73. }
  74. } else if (d->m_operator == OR) {
  75. for (int i = 0; i < d->m_args.length( ); i++) {
  76. QSet<QString> r = d->m_args[i].getMatchedObjectClasses();
  77. if ( !r.empty() ) {
  78. objClasses += r;
  79. } else {
  80. objClasses.clear();
  81. break;
  82. }
  83. }
  84. }
  85. return objClasses;
  86. }
  87. bool ctkLDAPExpr::isSimple(
  88. const QList<QString> &keywords,
  89. QHash<int, QList<QString> > &cache,
  90. bool matchCase ) const
  91. {
  92. if (d->m_operator == EQ) {
  93. int index;
  94. if ((index = keywords.indexOf(matchCase ? d->m_attrName : d->m_attrName.toLower())) >= 0 &&
  95. d->m_attrValue.indexOf(WILDCARD) < 0) {
  96. cache[index] += d->m_attrValue;
  97. return true;
  98. }
  99. } else if (d->m_operator == OR) {
  100. for (int i = 0; i < d->m_args.size( ); i++) {
  101. if (!d->m_args[i].isSimple(keywords, cache, matchCase))
  102. return false;
  103. }
  104. return true;
  105. }
  106. return false;
  107. }
  108. bool ctkLDAPExpr::query( const QString &filter, const ctkDictionary &pd ) throw (ctkInvalidSyntaxException)
  109. {
  110. return ctkLDAPExpr(filter).evaluate(pd, false);
  111. }
  112. bool ctkLDAPExpr::evaluate( const ctkDictionary &p, bool matchCase ) const
  113. {
  114. if ((d->m_operator & SIMPLE) != 0) {
  115. return compare(p[ matchCase ? d->m_attrName : d->m_attrName.toLower() ],
  116. d->m_operator, d->m_attrValue);
  117. } else { // (d->m_operator & COMPLEX) != 0
  118. switch (d->m_operator) {
  119. case AND:
  120. for (int i = 0; i < d->m_args.length( ); i++) {
  121. if (!d->m_args[i].evaluate(p, matchCase))
  122. return false;
  123. }
  124. return true;
  125. case OR:
  126. for (int i = 0; i < d->m_args.length( ); i++) {
  127. if (d->m_args[i].evaluate(p, matchCase))
  128. return true;
  129. }
  130. return false;
  131. case NOT:
  132. return !d->m_args[0].evaluate(p, matchCase);
  133. default:
  134. return false; // Cannot happen
  135. }
  136. }
  137. }
  138. bool ctkLDAPExpr::compare( const QVariant &obj, int op, const QString &s ) const
  139. {
  140. if (obj.isNull())
  141. return false;
  142. if (op == EQ && s == WILDCARD_QString )
  143. return true;
  144. try {
  145. if ( obj.canConvert<QString>( ) ) {
  146. return compareQString(obj.toString(), op, s);
  147. } else if (obj.canConvert<char>( ) ) {
  148. return compareQString(obj.toString(), op, s);
  149. } else if (obj.canConvert<bool>( ) ) {
  150. if (op==LE || op==GE)
  151. return false;
  152. if ( obj.toBool() ) {
  153. return s.compare("true", Qt::CaseInsensitive);
  154. } else {
  155. return s.compare("false", Qt::CaseInsensitive);
  156. }
  157. }
  158. else if ( obj.canConvert<Byte>( ) || obj.canConvert<int>( ) )
  159. {
  160. switch(op) {
  161. case LE:
  162. return obj.toInt() <= s.toInt();
  163. case GE:
  164. return obj.toInt() >= s.toInt();
  165. default: /*APPROX and EQ*/
  166. return s.toInt( ) == obj.toInt();
  167. }
  168. } else if ( obj.canConvert<float>( ) ) {
  169. switch(op) {
  170. case LE:
  171. return obj.toFloat() <= s.toFloat();
  172. case GE:
  173. return obj.toFloat() >= s.toFloat();
  174. default: /*APPROX and EQ*/
  175. return s.toFloat() == obj.toFloat();
  176. }
  177. } else if (obj.canConvert<double>()) {
  178. switch(op) {
  179. case LE:
  180. return obj.toDouble() <= s.toDouble();
  181. case GE:
  182. return obj.toDouble() >= s.toDouble();
  183. default: /*APPROX and EQ*/
  184. return s.toDouble( ) == obj.toDouble( );
  185. }
  186. } else if (obj.canConvert<qlonglong>( )) {
  187. switch(op) {
  188. case LE:
  189. return obj.toLongLong() <= s.toLongLong( );
  190. case GE:
  191. return obj.toLongLong() >= s.toLongLong( );
  192. default: /*APPROX and EQ*/
  193. return obj.toLongLong() == s.toLongLong( );
  194. }
  195. }
  196. else if (obj.canConvert< QList<QVariant> >()) {
  197. QList<QVariant> list = obj.toList();
  198. QList<QVariant>::Iterator it;
  199. for (it=list.begin(); it != list.end( ); it++)
  200. if (compare(*it, op, s))
  201. return true;
  202. }
  203. } catch (...) {
  204. // This might happen if a QString-to-datatype conversion fails
  205. // Just consider it a false match and ignore the exception
  206. }
  207. return false;
  208. }
  209. bool ctkLDAPExpr::compareQString( const QString &s1, int op, const QString &s2 )
  210. {
  211. switch(op) {
  212. case LE:
  213. return s1.compare(s2) <= 0;
  214. case GE:
  215. return s1.compare(s2) >= 0;
  216. case EQ:
  217. return patSubstr(s1,s2);
  218. case APPROX:
  219. return fixupQString(s2) == fixupQString(s1);
  220. default:
  221. return false;
  222. }
  223. }
  224. const QString ctkLDAPExpr::fixupQString( const QString &s )
  225. {
  226. QString sb;
  227. int len = s.length();
  228. for(int i=0; i<len; i++) {
  229. QChar c = s.at(i);
  230. if (!c.isSpace()) {
  231. if (c.isUpper())
  232. c = c.toLower();
  233. sb.append(c);
  234. }
  235. }
  236. return sb;
  237. }
  238. bool ctkLDAPExpr::patSubstr( const QString &s, int si, const QString &pat, int pi )
  239. {
  240. if (pat.size( )-pi == 0)
  241. return s.size( )-si == 0;
  242. if (QChar( pat[pi] ) == WILDCARD ) {
  243. pi++;
  244. for (;;) {
  245. if (patSubstr( s, si, pat, pi))
  246. return true;
  247. if (s.size( )-si == 0)
  248. return false;
  249. si++;
  250. }
  251. } else {
  252. if (s.size( )-si==0){
  253. return false;
  254. }
  255. if(s[si]!=pat[pi]){
  256. return false;
  257. }
  258. return patSubstr( s, ++si, pat, ++pi);
  259. }
  260. }
  261. bool ctkLDAPExpr::patSubstr( const QString &s, const QString &pat )
  262. {
  263. return s.isNull() ? false : patSubstr(s,0,pat,0);
  264. }
  265. ctkLDAPExpr ctkLDAPExpr::parseExpr( ParseState &ps ) throw (ctkInvalidSyntaxException)
  266. {
  267. ps.skipWhite();
  268. if (!ps.prefix("("))
  269. ps.error(MALFORMED);
  270. int op;
  271. ps.skipWhite();
  272. QChar c = ps.peek();
  273. if ( c == '&') {
  274. op = AND;
  275. }else if ( c == '|' ){
  276. op = OR;
  277. } else if ( c == '!' ) {
  278. op = NOT;
  279. } else {
  280. return parseSimple(ps);
  281. }
  282. ps.skip(1); // Ignore the d->m_operator
  283. QList<ctkLDAPExpr> v;
  284. do {
  285. v.append(parseExpr(ps));
  286. ps.skipWhite();
  287. } while (ps.peek() == '(');
  288. int n = v.size();
  289. if (!ps.prefix(")") || n == 0 || (op == NOT && n > 1))
  290. ps.error(MALFORMED);
  291. return ctkLDAPExpr(op, v);
  292. }
  293. ctkLDAPExpr ctkLDAPExpr::parseSimple( ParseState &ps ) throw (ctkInvalidSyntaxException)
  294. {
  295. QString attrName = ps.getAttributeName();
  296. if (attrName.isNull())
  297. ps.error(MALFORMED);
  298. int op = 0;
  299. if (ps.prefix("="))
  300. op = EQ;
  301. else if (ps.prefix("<="))
  302. op = LE;
  303. else if(ps.prefix(">="))
  304. op = GE;
  305. else if(ps.prefix("~="))
  306. op = APPROX;
  307. else {
  308. // System.out.println("undef op='" + ps.peek() + "'");
  309. ps.error(OPERATOR); // Does not return
  310. }
  311. QString attrValue = ps.getAttributeValue();
  312. if (!ps.prefix(")"))
  313. ps.error(MALFORMED);
  314. return ctkLDAPExpr(op, attrName, attrValue);
  315. }
  316. const QString ctkLDAPExpr::toQString() const
  317. {
  318. QString res;
  319. res.append("(");
  320. if ((d->m_operator & SIMPLE) != 0) {
  321. res.append(d->m_attrName);
  322. switch (d->m_operator) {
  323. case EQ:
  324. res.append("=");
  325. break;
  326. case LE:
  327. res.append("<=");
  328. break;
  329. case GE:
  330. res.append(">=");
  331. break;
  332. case APPROX:
  333. res.append("~=");
  334. break;
  335. }
  336. for (int i = 0; i < d->m_attrValue.length(); i++) {
  337. QChar c = d->m_attrValue.at(i);
  338. if (c == '(' || c == ')' || c == '*' || c == '\\') {
  339. res.append('\\');
  340. } else if (c == WILDCARD) {
  341. c = '*';
  342. }
  343. res.append(c);
  344. }
  345. } else {
  346. switch (d->m_operator) {
  347. case AND:
  348. res.append("&");
  349. break;
  350. case OR:
  351. res.append("|");
  352. break;
  353. case NOT:
  354. res.append("!");
  355. break;
  356. }
  357. for (int i = 0; i < d->m_args.length( ); i++) {
  358. res.append(d->m_args[i].toQString());
  359. }
  360. }
  361. res.append(")");
  362. return res;
  363. }
  364. ctkLDAPExpr::ParseState::ParseState( const QString &str ) throw (ctkInvalidSyntaxException)
  365. {
  366. m_str = str;
  367. if (m_str.length() == 0)
  368. error(NULLQ);
  369. m_pos = 0;
  370. }
  371. bool ctkLDAPExpr::ParseState::prefix( const QString &pre )
  372. {
  373. if (!m_str.startsWith(pre.mid(m_pos)))
  374. return false;
  375. m_pos += pre.length();
  376. return true;
  377. }
  378. QChar ctkLDAPExpr::ParseState::peek()
  379. {
  380. if ( m_pos >= m_str.size() )
  381. {
  382. throw std::out_of_range( "LDAPExpr" );
  383. }
  384. return m_str.at(m_pos);
  385. }
  386. void ctkLDAPExpr::ParseState::skip( int n )
  387. {
  388. m_pos += n;
  389. }
  390. const QString ctkLDAPExpr::ParseState::rest()
  391. {
  392. return m_str.mid(m_pos);
  393. }
  394. void ctkLDAPExpr::ParseState::skipWhite()
  395. {
  396. while ( peek( ).isSpace( ) ) {
  397. m_pos++;
  398. }
  399. }
  400. const QString ctkLDAPExpr::ParseState::getAttributeName()
  401. {
  402. int start = m_pos;
  403. int n = -1;
  404. for(;; m_pos++) {
  405. QChar c = peek( );
  406. if (c == '(' || c == ')' ||
  407. c == '<' || c == '>' ||
  408. c == '=' || c == '~') {
  409. break;
  410. } else if ( !c.isSpace( ) ) {
  411. n = m_pos - start + 1;
  412. }
  413. }
  414. if (n == -1) {
  415. return QString::Null( );
  416. }
  417. return m_str.mid(start, n);
  418. }
  419. const QString ctkLDAPExpr::ParseState::getAttributeValue()
  420. {
  421. QString sb;
  422. bool exit = false;
  423. while( !exit ) {
  424. QChar c = peek( );
  425. switch(c.toLatin1()) {
  426. case '(':
  427. case ')':
  428. exit = true;
  429. break;
  430. case '*':
  431. sb.append(WILDCARD);
  432. break;
  433. case '\\':
  434. sb.append(m_str.at(++m_pos));
  435. break;
  436. default:
  437. sb.append(c);
  438. break;
  439. }
  440. if ( !exit )
  441. {
  442. m_pos++;
  443. }
  444. }
  445. return sb;
  446. }
  447. void ctkLDAPExpr::ParseState::error( const QString &m ) throw (ctkInvalidSyntaxException)
  448. {
  449. QString error = m + ": " + (m_str.isNull() ? "" : m_str.mid(m_pos) );
  450. throw ctkInvalidSyntaxException( error.toStdString() );
  451. }