/plugins/TextTools/tags/release-1_12/JSort.java

# · Java · 527 lines · 293 code · 55 blank · 179 comment · 50 complexity · f084f3e2e1c188af3d07e0d9585048c0 MD5 · raw file

  1. /*
  2. * :folding=explicit:collapseFolds=1:
  3. *
  4. * JSort.java - a class to sort sets
  5. * Copyright (c) 2002 Carmine Lucarelli (carmine.lucarelli@rogers.com)
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. */
  21. //{{{ imports
  22. import java.util.*;
  23. import org.gjt.sp.jedit.BeanShell;
  24. import org.gjt.sp.util.Log;
  25. //}}}
  26. /**
  27. * Main sort routines
  28. *
  29. * @author <A HREF="mailto:carmine.lucarelli@rogers.com">Carmine Lucarelli</A>
  30. */
  31. public class JSort implements Comparator
  32. {
  33. //{{{ private members
  34. /**
  35. * A collection of SortBy objects (sort options)
  36. */
  37. private Vector options;
  38. /**
  39. * Flags to control whether we will delete duplicate entries while we sort / skip sort
  40. */
  41. private boolean deleteDuplicates;
  42. private boolean dontSort;
  43. /**
  44. * The sorted data
  45. */
  46. private TreeSet data;
  47. //}}}
  48. //{{{ JSort constructors
  49. /**
  50. * Constructor initializes Vector of SortBy instances and
  51. * sets properties to false;
  52. *
  53. */
  54. public JSort()
  55. {
  56. this(false, false);
  57. }
  58. /**
  59. * Constructor initializes Vector of SortBy instances and
  60. * sets deleteDuplicates property to value passed in;
  61. *
  62. * @param deleteDuplicates Should we delete duplicate lines while we sort?
  63. *
  64. */
  65. public JSort(boolean deleteDuplicates)
  66. {
  67. this(deleteDuplicates, false);
  68. } //}}}
  69. /**
  70. * Constructor initializes Vector of SortBy instances and
  71. * sets properties to values passed in;
  72. *
  73. * @param deleteDuplicates Should we delete duplicate lines while we sort?
  74. * @param dontSort Should we skip sorting when deleting duplicates?
  75. *
  76. */
  77. public JSort(boolean deleteDuplicates, boolean dontSort)
  78. {
  79. options = new Vector();
  80. this.deleteDuplicates = deleteDuplicates;
  81. this.dontSort = dontSort;
  82. } //}}}
  83. //{{{ methods to modify sort
  84. //{{{ getSortBy()
  85. /**
  86. * convenience method to return all current options for this sort
  87. */
  88. public Vector getSortBy()
  89. {
  90. return options;
  91. } //}}}
  92. //{{{ addSortBy(JSort.SortBy sortBy)
  93. public void addSortBy(JSort.SortBy sortBy)
  94. {
  95. options.add(sortBy);
  96. } //}}}
  97. //{{{ clearSort
  98. /**
  99. * remove all current sort options and set delete duplicates flag to false
  100. */
  101. public void clearSort()
  102. {
  103. options = new Vector();
  104. deleteDuplicates = false;
  105. dontSort = false;
  106. } //}}}
  107. //{{{ JSort addSortConstraint
  108. /**
  109. * Add a 'field' to sort by. Identical to calling addSortConstraint(startColumn, endColumn, true, false, false, false)
  110. *
  111. * @param startColumn The start position of the field, 1 indexed. If less than one, will be
  112. * modified to be one.
  113. * @param endColumn The end position of the field, inclusive and 1 indexed. If greater than
  114. * the length of any of the entries, will effectively be startColumn to the
  115. * end of the entry.
  116. */
  117. public void addSortConstraint(int startColumn, int endColumn)
  118. {
  119. addSortConstraint(startColumn, endColumn, true, false, false, false, false);
  120. }
  121. /**
  122. * Add a 'field' to sort by. Identical to calling addSortConstraint(startColumn, endColumn, ascending, false, false, false)
  123. *
  124. * @param startColumn The start position of the field, 1 indexed. If less than one, will be
  125. * modified to be one.
  126. * @param endColumn The end position of the field, inclusive and 1 indexed. If greater than
  127. * the length of any of the entries, will effectively be startColumn to the
  128. * end of the entry.
  129. * @param ascending True for an ascending sort, false for a descending
  130. */
  131. public void addSortConstraint(int startColumn, int endColumn, boolean ascending)
  132. {
  133. addSortConstraint(startColumn, endColumn, ascending, false, false, false, false);
  134. }
  135. /**
  136. * Add a 'field' to sort by. Identical to calling addSortConstraint(startColumn, endColumn, ascending, ignoreCase, false, false)
  137. *
  138. * @param startColumn The start position of the field, 1 indexed. If less than one, will be
  139. * modified to be one.
  140. * @param endColumn The end position of the field, inclusive and 1 indexed. If greater than
  141. * the length of any of the entries, will effectively be startColumn to the
  142. * end of the entry.
  143. * @param ascending True for an ascending sort, false for a descending
  144. * @param ignoreCase True will treat 'a' and 'A' identically.
  145. */
  146. public void addSortConstraint(int startColumn, int endColumn, boolean ascending, boolean ignoreCase)
  147. {
  148. addSortConstraint(startColumn, endColumn, ascending, ignoreCase, false, false, false);
  149. }
  150. /**
  151. * Add a 'field' to sort by. Identical to calling addSortConstraint(startColumn, endColumn, ascending, ignoreCase, numeric, false)
  152. *
  153. * @param startColumn The start position of the field, 1 indexed. If less than one, will be
  154. * modified to be one.
  155. * @param endColumn The end position of the field, inclusive and 1 indexed. If greater than
  156. * the length of any of the entries, will effectively be startColumn to the
  157. * end of the entry.
  158. * @param ascending True for an ascending sort, false for a descending
  159. * @param ignoreCase True will treat 'a' and 'A' identically.
  160. * @param numeric True will place SomeName2 before SomeName10 in the sort.
  161. */
  162. public void addSortConstraint(int startColumn, int endColumn, boolean ascending, boolean ignoreCase, boolean numeric)
  163. {
  164. addSortConstraint(startColumn, endColumn, ascending, ignoreCase, numeric, false, false);
  165. } //}}}
  166. /**
  167. * Add a 'field' to sort by. The entries are used in order. So the first
  168. * constraint is the primary sort field, the second becomes the secondary, etc.
  169. *
  170. * @param startColumn The start position of the field, 1 indexed. If less than one, will be
  171. * modified to be one.
  172. * @param endColumn The end position of the field, inclusive and 1 indexed. If greater than
  173. * the length of any of the entries, will effectively be startColumn to the
  174. * end of the entry.
  175. * @param ascending True for an ascending sort, false for a descending
  176. * @param ignoreCase True will treat 'a' and 'A' identically.
  177. * @param numeric True will place SomeName2 before SomeName10 in the sort.
  178. * @param trimWhitespace True will do a String.trim() previous to sorting (this will not trim the original
  179. * value in the List to be sorted
  180. */
  181. public void addSortConstraint(int startColumn, int endColumn, boolean ascending, boolean ignoreCase, boolean numeric, boolean trimWhitespace, boolean delDupRange)
  182. {
  183. // column choices are 1 indexed...
  184. addSortBy((new JSort.SortBy(--startColumn, endColumn, ascending, ignoreCase, numeric, trimWhitespace, delDupRange)));
  185. } //}}}
  186. //{{{ set/get DeleteDuplicates(boolean)
  187. public void setDeleteDuplicates(boolean deleteDuplicates)
  188. {
  189. this.deleteDuplicates = deleteDuplicates;
  190. }
  191. public boolean getDeleteDuplicates()
  192. {
  193. return deleteDuplicates;
  194. }
  195. //}}}
  196. //{{{ set/get dontSort(boolean)
  197. public void setDontSort(boolean dontSort)
  198. {
  199. this.dontSort = dontSort;
  200. }
  201. public boolean getDontSort()
  202. {
  203. return dontSort;
  204. }
  205. //}}}
  206. //{{{ public sort methods
  207. //{{{ shuffle(List)
  208. /**
  209. * Randomize the entries in the given collection
  210. *
  211. * @param list The data to 'shuffle'
  212. */
  213. public static void shuffle(List list)
  214. {
  215. for(int lastPlace = list.size() - 1; lastPlace > 0; lastPlace--)
  216. {
  217. // Choose a random location from among 0,1,...,lastPlace.
  218. int randLoc = (int)(Math.random() * (lastPlace + 1));
  219. // Swap items in locations randLoc and lastPlace.
  220. Object o = list.set(lastPlace, list.get(randLoc));
  221. list.set(randLoc, o);
  222. }
  223. } //}}}
  224. //{{{ sort(List)
  225. /**
  226. * Sort the given collection, based on current sort options.
  227. *
  228. * @param list The data to sort
  229. */
  230. public void sort(List list)
  231. {
  232. if(options.size() == 0)
  233. {
  234. addSortConstraint(0, 10000);
  235. }
  236. TreeSet ts = new TreeSet(this);
  237. ts.addAll(list);
  238. list.clear();
  239. list.addAll(ts);
  240. } //}}}
  241. //}}}
  242. //{{{ compare methods
  243. //{{{ compare(Object, Object)
  244. /**
  245. * Implemenation of java.util.Comparator used in creating a sorted
  246. * collection (TreeMap implementation). Will also delete duplicates
  247. * if option is set to true.
  248. *
  249. * @return int if o1 > o2, positive, 0 if same and delete duplicates option set, else negative
  250. */
  251. // public static int dontSort = 1;
  252. public int compare(Object o1, Object o2)
  253. {
  254. // if they don't pass in strings, sort 'em anyway
  255. String s1 = o1.toString(), s2 = o2.toString();
  256. if(deleteDuplicates && s1.equals(s2))
  257. return 0;
  258. int retVal = 0;
  259. for(int i = 0; i < options.size(); i++)
  260. {
  261. SortBy sb = (SortBy)options.elementAt(i);
  262. if(sb.startColumn < 0)
  263. {
  264. sb.startColumn = 0;
  265. }
  266. String sub1;
  267. String sub2;
  268. if (sb.endColumn == 0) {
  269. sub1 = s1;
  270. sub2 = s2;
  271. } else {
  272. sub1 = getCompareStringForSortby(sb, s1);
  273. sub2 = getCompareStringForSortby(sb, s2);
  274. }
  275. if(sb.trimWhitespace)
  276. {
  277. sub1 = sub1.trim();
  278. sub2 = sub2.trim();
  279. }
  280. retVal = compare(sub1, sub2, sb);
  281. if (TextToolsPlugin.debugTT) Log.log(Log.DEBUG, BeanShell.class,"JSort.298: retVal = "+retVal+", sub1 = "+sub1
  282. +", sub2 = "+sub2+", sb = "+sb);
  283. if(retVal == 0)
  284. {
  285. // rwadd: delete, if section equal and deldup selected
  286. if (sb.delDupRange)
  287. return 0;
  288. else {
  289. if (dontSort) return 1; // if no sorting required, make 'bigger'
  290. continue;
  291. }
  292. }
  293. if (dontSort) return 1; // if no sorting required, make 'bigger'
  294. break;
  295. }
  296. if(retVal == 0)
  297. {
  298. // if we returned zero, the item wouldn't be added to the TreeSet, so
  299. // in an attempt to retain original order, make 'bigger'
  300. return 1;
  301. }
  302. return retVal;
  303. } //}}}
  304. //{{{ compare(String, String, SortBy)
  305. /**
  306. *********** originally copied from Slava Pestov's MiscUtilities.compareStrings(...) method ***********
  307. * A more intelligent version of String.compareTo() that handles
  308. * numbers specially. For example, it places "My file 2" before
  309. * "My file 10". Will also sort ascending or descending.
  310. *
  311. * @param str1 The first string
  312. * @param str2 The second string
  313. * @param sortBy The options for this sort (ascending/descending, numeric sort, ignore case)
  314. * @return negative If str1 &lt; str2, 0 if both are the same,
  315. * positive if str1 &gt; str2
  316. */
  317. public int compare(String str1, String str2, SortBy sortBy)
  318. {
  319. char[] char1 = str1.toCharArray();
  320. char[] char2 = str2.toCharArray();
  321. int len = Math.min(char1.length,char2.length);
  322. for(int i = 0, j = 0; i < len && j < len; i++, j++)
  323. {
  324. char ch1 = char1[i];
  325. char ch2 = char2[j];
  326. if(sortBy.numeric && Character.isDigit(ch1) && Character.isDigit(ch2)
  327. && ch1 != '0' && ch2 != '0')
  328. {
  329. int _i = i + 1;
  330. int _j = j + 1;
  331. for(; _i < char1.length; _i++)
  332. {
  333. if(!Character.isDigit(char1[_i]))
  334. {
  335. //_i--;
  336. break;
  337. }
  338. }
  339. for(; _j < char2.length; _j++)
  340. {
  341. if(!Character.isDigit(char2[_j]))
  342. {
  343. //_j--;
  344. break;
  345. }
  346. }
  347. int len1 = _i - i;
  348. int len2 = _j - j;
  349. if(len1 > len2)
  350. {
  351. if(sortBy.ascending)
  352. {
  353. return 1;
  354. }
  355. else
  356. {
  357. return -1;
  358. }
  359. }
  360. else if(len1 < len2)
  361. {
  362. if(sortBy.ascending)
  363. {
  364. return -1;
  365. }
  366. else
  367. {
  368. return 1;
  369. }
  370. }
  371. else
  372. {
  373. for(int k = 0; k < len1; k++)
  374. {
  375. ch1 = char1[i + k];
  376. ch2 = char2[j + k];
  377. if(ch1 != ch2)
  378. {
  379. if(sortBy.ascending)
  380. {
  381. return (ch1 - ch2);
  382. }
  383. else
  384. {
  385. return ((ch1 - ch2) * -1);
  386. }
  387. }
  388. }
  389. }
  390. i = _i - 1;
  391. j = _j - 1;
  392. }
  393. else
  394. {
  395. if(sortBy.ignoreCase)
  396. {
  397. ch1 = Character.toLowerCase(ch1);
  398. ch2 = Character.toLowerCase(ch2);
  399. }
  400. if(ch1 != ch2)
  401. {
  402. if(sortBy.ascending)
  403. {
  404. return (ch1 - ch2);
  405. }
  406. else
  407. {
  408. return ((ch1 - ch2) * -1);
  409. }
  410. }
  411. }
  412. }
  413. if(sortBy.ascending)
  414. {
  415. return (char1.length - char2.length);
  416. }
  417. else
  418. {
  419. return ((char1.length - char2.length) * -1);
  420. }
  421. } //}}}
  422. //}}}
  423. //{{{ SortBy class def.
  424. public class SortBy
  425. {
  426. public int startColumn;
  427. public int endColumn;
  428. public boolean ascending;
  429. public boolean ignoreCase;
  430. public boolean numeric;
  431. public boolean trimWhitespace;
  432. public boolean delDupRange;
  433. public SortBy(int startColumn, int endColumn, boolean ascending, boolean
  434. ignoreCase, boolean numeric, boolean trimWhitespace, boolean delDupRange)
  435. {
  436. this.startColumn = startColumn;
  437. this.endColumn = endColumn;
  438. this.ascending = ascending;
  439. this.ignoreCase = ignoreCase;
  440. this.numeric = numeric;
  441. this.trimWhitespace = trimWhitespace;
  442. this.delDupRange = delDupRange;
  443. }
  444. public String toString()
  445. {
  446. StringBuffer sb = new StringBuffer();
  447. sb.append("startColumn = ").append(startColumn)
  448. .append(" endColumn = ").append(endColumn)
  449. .append(" ascending = ").append(ascending)
  450. .append(" ignoreCase = ").append(ignoreCase)
  451. .append(" numeric = ").append(numeric)
  452. .append(" trimWhitespace = ").append(trimWhitespace)
  453. .append(" delDupRange = ").append(delDupRange);
  454. return sb.toString();
  455. }
  456. } //}}}
  457. private static String getCompareStringForSortby(SortBy sb, String compStr)
  458. {
  459. if(sb.startColumn > compStr.length())
  460. return new String();
  461. else {
  462. if(sb.endColumn > compStr.length())
  463. {
  464. return compStr.substring(sb.startColumn, compStr.length());
  465. }
  466. else
  467. {
  468. return compStr.substring(sb.startColumn, sb.endColumn);
  469. }
  470. }
  471. }
  472. }