PageRenderTime 59ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/TCL/src/commands/LsearchCmd.cs

https://bitbucket.org/eumario/csharp-sqlite
C# | 510 lines | 356 code | 60 blank | 94 comment | 83 complexity | 8b17d7176c0f48b1918dff111b91b448 MD5 | raw file
  1. /*
  2. * LsearchCmd.java
  3. *
  4. * Copyright (c) 1997 Cornell University.
  5. * Copyright (c) 1997 Sun Microsystems, Inc.
  6. * Copyright (c) 1998-1999 by Scriptics Corporation.
  7. * Copyright (c) 2000 Christian Krone.
  8. *
  9. * See the file "license.terms" for information on usage and
  10. * redistribution of this file, and for a DISCLAIMER OF ALL
  11. * WARRANTIES.
  12. *
  13. * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart
  14. *
  15. * RCS @(#) $Id: LsearchCmd.java,v 1.2 2000/08/21 04:12:51 mo Exp $
  16. *
  17. */
  18. using System;
  19. namespace tcl.lang
  20. {
  21. /*
  22. * This class implements the built-in "lsearch" command in Tcl.
  23. */
  24. class LsearchCmd : Command
  25. {
  26. private static readonly string[] options = new string[] { "-ascii", "-decreasing", "-dictionary", "-exact", "-increasing", "-integer", "-glob", "-real", "-regexp", "-sorted" };
  27. internal const int LSEARCH_ASCII = 0;
  28. internal const int LSEARCH_DECREASING = 1;
  29. internal const int LSEARCH_DICTIONARY = 2;
  30. internal const int LSEARCH_EXACT = 3;
  31. internal const int LSEARCH_INCREASING = 4;
  32. internal const int LSEARCH_INTEGER = 5;
  33. internal const int LSEARCH_GLOB = 6;
  34. internal const int LSEARCH_REAL = 7;
  35. internal const int LSEARCH_REGEXP = 8;
  36. internal const int LSEARCH_SORTED = 9;
  37. internal const int ASCII = 0;
  38. internal const int DICTIONARY = 1;
  39. internal const int INTEGER = 2;
  40. internal const int REAL = 3;
  41. internal const int EXACT = 0;
  42. internal const int GLOB = 1;
  43. internal const int REGEXP = 2;
  44. internal const int SORTED = 3;
  45. /*
  46. *-----------------------------------------------------------------------------
  47. *
  48. * cmdProc --
  49. *
  50. * This procedure is invoked to process the "lsearch" Tcl command.
  51. * See the user documentation for details on what it does.
  52. *
  53. * Results:
  54. * None.
  55. *
  56. * Side effects:
  57. * See the user documentation.
  58. *
  59. *-----------------------------------------------------------------------------
  60. */
  61. public TCL.CompletionCode cmdProc( Interp interp, TclObject[] objv )
  62. {
  63. int mode = GLOB;
  64. int dataType = ASCII;
  65. bool isIncreasing = true;
  66. TclObject pattern;
  67. TclObject list;
  68. if ( objv.Length < 3 )
  69. {
  70. throw new TclNumArgsException( interp, 1, objv, "?options? list pattern" );
  71. }
  72. for ( int i = 1; i < objv.Length - 2; i++ )
  73. {
  74. switch ( TclIndex.get( interp, objv[i], options, "option", 0 ) )
  75. {
  76. case LSEARCH_ASCII:
  77. dataType = ASCII;
  78. break;
  79. case LSEARCH_DECREASING:
  80. isIncreasing = false;
  81. break;
  82. case LSEARCH_DICTIONARY:
  83. dataType = DICTIONARY;
  84. break;
  85. case LSEARCH_EXACT:
  86. mode = EXACT;
  87. break;
  88. case LSEARCH_INCREASING:
  89. isIncreasing = true;
  90. break;
  91. case LSEARCH_INTEGER:
  92. dataType = INTEGER;
  93. break;
  94. case LSEARCH_GLOB:
  95. mode = GLOB;
  96. break;
  97. case LSEARCH_REAL:
  98. dataType = REAL;
  99. break;
  100. case LSEARCH_REGEXP:
  101. mode = REGEXP;
  102. break;
  103. case LSEARCH_SORTED:
  104. mode = SORTED;
  105. break;
  106. }
  107. }
  108. // Make sure the list argument is a list object and get its length and
  109. // a pointer to its array of element pointers.
  110. TclObject[] listv = TclList.getElements( interp, objv[objv.Length - 2] );
  111. TclObject patObj = objv[objv.Length - 1];
  112. string patternBytes = null;
  113. int patInt = 0;
  114. double patDouble = 0.0;
  115. int length = 0;
  116. if ( mode == EXACT || mode == SORTED )
  117. {
  118. switch ( dataType )
  119. {
  120. case ASCII:
  121. case DICTIONARY:
  122. patternBytes = patObj.ToString();
  123. length = patternBytes.Length;
  124. break;
  125. case INTEGER:
  126. patInt = TclInteger.get( interp, patObj );
  127. break;
  128. case REAL:
  129. patDouble = TclDouble.get( interp, patObj );
  130. break;
  131. }
  132. }
  133. else
  134. {
  135. patternBytes = patObj.ToString();
  136. length = patternBytes.Length;
  137. }
  138. // Set default index value to -1, indicating failure; if we find the
  139. // item in the course of our search, index will be set to the correct
  140. // value.
  141. int index = -1;
  142. if ( mode == SORTED )
  143. {
  144. // If the data is sorted, we can do a more intelligent search.
  145. int match = 0;
  146. int lower = -1;
  147. int upper = listv.Length;
  148. while ( lower + 1 != upper )
  149. {
  150. int i = ( lower + upper ) / 2;
  151. switch ( dataType )
  152. {
  153. case ASCII:
  154. {
  155. string bytes = listv[i].ToString();
  156. match = patternBytes.CompareTo( bytes );
  157. break;
  158. }
  159. case DICTIONARY:
  160. {
  161. string bytes = listv[i].ToString();
  162. match = DictionaryCompare( patternBytes, bytes );
  163. break;
  164. }
  165. case INTEGER:
  166. {
  167. int objInt = TclInteger.get( interp, listv[i] );
  168. if ( patInt == objInt )
  169. {
  170. match = 0;
  171. }
  172. else if ( patInt < objInt )
  173. {
  174. match = -1;
  175. }
  176. else
  177. {
  178. match = 1;
  179. }
  180. break;
  181. }
  182. case REAL:
  183. {
  184. double objDouble = TclDouble.get( interp, listv[i] );
  185. if ( patDouble == objDouble )
  186. {
  187. match = 0;
  188. }
  189. else if ( patDouble < objDouble )
  190. {
  191. match = -1;
  192. }
  193. else
  194. {
  195. match = 1;
  196. }
  197. break;
  198. }
  199. }
  200. if ( match == 0 )
  201. {
  202. // Normally, binary search is written to stop when it
  203. // finds a match. If there are duplicates of an element in
  204. // the list, our first match might not be the first occurance.
  205. // Consider: 0 0 0 1 1 1 2 2 2
  206. // To maintain consistancy with standard lsearch semantics,
  207. // we must find the leftmost occurance of the pattern in the
  208. // list. Thus we don't just stop searching here. This
  209. // variation means that a search always makes log n
  210. // comparisons (normal binary search might "get lucky" with
  211. // an early comparison).
  212. index = i;
  213. upper = i;
  214. }
  215. else if ( match > 0 )
  216. {
  217. if ( isIncreasing )
  218. {
  219. lower = i;
  220. }
  221. else
  222. {
  223. upper = i;
  224. }
  225. }
  226. else
  227. {
  228. if ( isIncreasing )
  229. {
  230. upper = i;
  231. }
  232. else
  233. {
  234. lower = i;
  235. }
  236. }
  237. }
  238. }
  239. else
  240. {
  241. for ( int i = 0; i < listv.Length; i++ )
  242. {
  243. bool match = false;
  244. switch ( mode )
  245. {
  246. case SORTED:
  247. case EXACT:
  248. {
  249. switch ( dataType )
  250. {
  251. case ASCII:
  252. {
  253. string bytes = listv[i].ToString();
  254. int elemLen = bytes.Length;
  255. if ( length == elemLen )
  256. {
  257. match = bytes.Equals( patternBytes );
  258. }
  259. break;
  260. }
  261. case DICTIONARY:
  262. {
  263. string bytes = listv[i].ToString();
  264. match = ( DictionaryCompare( bytes, patternBytes ) == 0 );
  265. break;
  266. }
  267. case INTEGER:
  268. {
  269. int objInt = TclInteger.get( interp, listv[i] );
  270. match = ( objInt == patInt );
  271. break;
  272. }
  273. case REAL:
  274. {
  275. double objDouble = TclDouble.get( interp, listv[i] );
  276. match = ( objDouble == patDouble );
  277. break;
  278. }
  279. }
  280. break;
  281. }
  282. case GLOB:
  283. {
  284. match = Util.stringMatch( listv[i].ToString(), patternBytes );
  285. break;
  286. }
  287. case REGEXP:
  288. {
  289. match = Util.regExpMatch( interp, listv[i].ToString(), patObj );
  290. break;
  291. }
  292. }
  293. if ( match )
  294. {
  295. index = i;
  296. break;
  297. }
  298. }
  299. }
  300. interp.setResult( index );
  301. return TCL.CompletionCode.RETURN;
  302. }
  303. /*
  304. *----------------------------------------------------------------------
  305. *
  306. * DictionaryCompare -> dictionaryCompare
  307. *
  308. * This function compares two strings as if they were being used in
  309. * an index or card catalog. The case of alphabetic characters is
  310. * ignored, except to break ties. Thus "B" comes before "b" but
  311. * after "a". Also, integers embedded in the strings compare in
  312. * numerical order. In other words, "x10y" comes after "x9y", not
  313. * before it as it would when using strcmp().
  314. *
  315. * Results:
  316. * A negative result means that the first element comes before the
  317. * second, and a positive result means that the second element
  318. * should come first. A result of zero means the two elements
  319. * are equal and it doesn't matter which comes first.
  320. *
  321. * Side effects:
  322. * None.
  323. *
  324. *----------------------------------------------------------------------
  325. */
  326. private static int DictionaryCompare( string left, string right )
  327. // The strings to compare
  328. {
  329. char[] leftArr = left.ToCharArray();
  330. char[] rightArr = right.ToCharArray();
  331. char leftChar, rightChar, leftLower, rightLower;
  332. int lInd = 0;
  333. int rInd = 0;
  334. int diff;
  335. int secondaryDiff = 0;
  336. while ( true )
  337. {
  338. if ( ( rInd < rightArr.Length ) && ( System.Char.IsDigit( rightArr[rInd] ) ) && ( lInd < leftArr.Length ) && ( System.Char.IsDigit( leftArr[lInd] ) ) )
  339. {
  340. // There are decimal numbers embedded in the two
  341. // strings. Compare them as numbers, rather than
  342. // strings. If one number has more leading zeros than
  343. // the other, the number with more leading zeros sorts
  344. // later, but only as a secondary choice.
  345. int zeros = 0;
  346. while ( ( rightArr[rInd] == '0' ) && ( rInd + 1 < rightArr.Length ) && ( System.Char.IsDigit( rightArr[rInd + 1] ) ) )
  347. {
  348. rInd++;
  349. zeros--;
  350. }
  351. while ( ( leftArr[lInd] == '0' ) && ( lInd + 1 < leftArr.Length ) && ( System.Char.IsDigit( leftArr[lInd + 1] ) ) )
  352. {
  353. lInd++;
  354. zeros++;
  355. }
  356. if ( secondaryDiff == 0 )
  357. {
  358. secondaryDiff = zeros;
  359. }
  360. // The code below compares the numbers in the two
  361. // strings without ever converting them to integers. It
  362. // does this by first comparing the lengths of the
  363. // numbers and then comparing the digit values.
  364. diff = 0;
  365. while ( true )
  366. {
  367. if ( ( diff == 0 ) && ( lInd < leftArr.Length ) && ( rInd < rightArr.Length ) )
  368. {
  369. diff = leftArr[lInd] - rightArr[rInd];
  370. }
  371. rInd++;
  372. lInd++;
  373. if ( rInd >= rightArr.Length || !System.Char.IsDigit( rightArr[rInd] ) )
  374. {
  375. if ( lInd < leftArr.Length && System.Char.IsDigit( leftArr[lInd] ) )
  376. {
  377. return 1;
  378. }
  379. else
  380. {
  381. // The two numbers have the same length. See
  382. // if their values are different.
  383. if ( diff != 0 )
  384. {
  385. return diff;
  386. }
  387. break;
  388. }
  389. }
  390. else if ( lInd >= leftArr.Length || !System.Char.IsDigit( leftArr[lInd] ) )
  391. {
  392. return -1;
  393. }
  394. }
  395. continue;
  396. }
  397. // Convert character to Unicode for comparison purposes. If either
  398. // string is at the terminating null, do a byte-wise comparison and
  399. // bail out immediately.
  400. if ( ( lInd < leftArr.Length ) && ( rInd < rightArr.Length ) )
  401. {
  402. // Convert both chars to lower for the comparison, because
  403. // dictionary sorts are case insensitve. Covert to lower, not
  404. // upper, so chars between Z and a will sort before A (where most
  405. // other interesting punctuations occur)
  406. leftChar = leftArr[lInd++];
  407. rightChar = rightArr[rInd++];
  408. leftLower = System.Char.ToLower( leftChar );
  409. rightLower = System.Char.ToLower( rightChar );
  410. }
  411. else if ( lInd < leftArr.Length )
  412. {
  413. diff = -rightArr[rInd];
  414. break;
  415. }
  416. else if ( rInd < rightArr.Length )
  417. {
  418. diff = leftArr[lInd];
  419. break;
  420. }
  421. else
  422. {
  423. diff = 0;
  424. break;
  425. }
  426. diff = leftLower - rightLower;
  427. if ( diff != 0 )
  428. {
  429. return diff;
  430. }
  431. else if ( secondaryDiff == 0 )
  432. {
  433. if ( System.Char.IsUpper( leftChar ) && System.Char.IsLower( rightChar ) )
  434. {
  435. secondaryDiff = -1;
  436. }
  437. else if ( System.Char.IsUpper( rightChar ) && System.Char.IsLower( leftChar ) )
  438. {
  439. secondaryDiff = 1;
  440. }
  441. }
  442. }
  443. if ( diff == 0 )
  444. {
  445. diff = secondaryDiff;
  446. }
  447. return diff;
  448. }
  449. } // end LsearchCmd
  450. }