PageRenderTime 66ms CodeModel.GetById 48ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

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

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