PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/plugins-trunk/TextTools/JSort.java

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