Permalink
Browse files

Issue #27 Implement missing sort options

  • Loading branch information...
terzerm committed Sep 20, 2016
1 parent cb4f081 commit 0ab496e429e1b91c7ec2c933197df9db8def8739
Showing with 1,005 additions and 110 deletions.
  1. +9 −0 unix4j-core/unix4j-base/src/main/java/org/unix4j/util/Java7Util.java
  2. +3 −2 unix4j-core/unix4j-base/src/main/java/org/unix4j/util/sort/DecimalNumberStringComparator.java
  3. +54 −0 unix4j-core/unix4j-base/src/main/java/org/unix4j/util/sort/DictionaryStringComparator.java
  4. +27 −37 unix4j-core/unix4j-base/src/main/java/org/unix4j/util/sort/LineComparator.java
  5. +105 −0 unix4j-core/unix4j-base/src/main/java/org/unix4j/util/sort/MonthStringComparator.java
  6. +104 −0 unix4j-core/unix4j-base/src/main/java/org/unix4j/util/sort/UnitsNumberStringComparator.java
  7. +109 −0 unix4j-core/unix4j-base/src/main/java/org/unix4j/util/sort/VersionStringComparator.java
  8. +5 −52 unix4j-core/unix4j-base/src/test/java/org/unix4j/util/AbstractNumberStringComparatorTest.java
  9. +41 −0 unix4j-core/unix4j-base/src/test/java/org/unix4j/util/AbstractStringComparatorTest.java
  10. +1 −0 unix4j-core/unix4j-base/src/test/java/org/unix4j/util/DecimalNumberStringComparatorTest.java
  11. +86 −0 unix4j-core/unix4j-base/src/test/java/org/unix4j/util/MonthStringComparatorTest.java
  12. +118 −0 unix4j-core/unix4j-base/src/test/java/org/unix4j/util/UnitsNumberStringComparatorTest.java
  13. +71 −0 unix4j-core/unix4j-base/src/test/java/org/unix4j/util/VersionStringComparatorTest.java
  14. +8 −17 unix4j-core/unix4j-command/src/main/java/org/unix4j/unix/sort/AbstractSortProcessor.java
  15. +264 −0 unix4j-core/unix4j-command/src/test/java/org/unix4j/unix/SortTest.java
  16. +0 −2 unix4j-tools/src/test/resources/sort.xml
@@ -11,6 +11,15 @@
*/
public static final String JAVA7_CLASS_NAME_SUFFIX = "7";
/**
* Returns true if the system property "java.version" starts with "1.7".
*
* @return true if the current java version is 1.7
*/
public static final boolean isJava7() {
return System.getProperty("java.version").startsWith("1.7");
}
/**
* Returns a new instance of the Java 7 version of the given class if it
* exists and can be instantiated. Otherwise, the given
@@ -3,6 +3,7 @@
import java.text.DecimalFormatSymbols;
import java.util.Comparator;
import java.util.Locale;
import java.util.Objects;
/**
* A comparator for decimal strings consisting of optional blanks, an optional
@@ -56,7 +57,7 @@ private DecimalNumberStringComparator(Locale locale) {
* the decimal symbols
*/
public DecimalNumberStringComparator(DecimalFormatSymbols symbols) {
this.symbols = symbols;
this.symbols = Objects.requireNonNull(symbols);
}
@Override
@@ -66,7 +67,7 @@ public int compare(CharSequence s1, CharSequence s2) {
return compare(s1, start1, s1.length(), s2, start2, s2.length());
}
private int compare(CharSequence s1, int start1, int end1, CharSequence s2, int start2, int end2) {
int compare(CharSequence s1, int start1, int end1, CharSequence s2, int start2, int end2) {
final char decimalSeparator = symbols.getDecimalSeparator();
final char groupingSeparator = symbols.getGroupingSeparator();
final char zeroDigit = symbols.getZeroDigit();
@@ -0,0 +1,54 @@
package org.unix4j.util.sort;
import java.util.Comparator;
import java.util.Objects;
/**
* Comparator for strings considering only blanks and alphanumeric characters.
*/
public class DictionaryStringComparator implements Comparator<String> {
private final Comparator<? super String> comparator;
/**
* Constructor with delegate comparator comparing strings containing only
* blanks and alphanumeric characters.
*
* @param comparator the delegate comparator
*/
public DictionaryStringComparator(final Comparator<? super String> comparator) {
this.comparator = Objects.requireNonNull(comparator);
}
@Override
public int compare(String s1, String s2) {
return comparator.compare(dictionaryString(s1), dictionaryString(s2));
}
private static String dictionaryString(String s) {
final int len = s.length();
for (int i = 0; i < len; i++) {
if (!isBlankOrAlphaNumeric(s.charAt(i))) {
return convertToDictionaryString(s);
}
}
return s;
}
private static String convertToDictionaryString(String s) {
final int len = s.length();
final StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
final char ch = s.charAt(i);
if (isBlankOrAlphaNumeric(ch)) {
sb.append(ch);
}
}
return sb.toString();
}
private static boolean isBlankOrAlphaNumeric(char ch) {
return Character.isWhitespace(ch) || Character.isDigit(ch) || Character.isLetter(ch);
}
}
@@ -1,11 +1,12 @@
package org.unix4j.util.sort;
import java.text.Collator;
import java.util.Comparator;
import org.unix4j.line.Line;
import org.unix4j.util.sort.TrimBlanksStringComparator.Mode;
import java.text.Collator;
import java.util.Comparator;
import java.util.Objects;
/**
* Comparator for a {@link Line} without line ending; forwards the comparison of
* the content string to a delegate string comparator. Some constants exist for
@@ -23,49 +24,38 @@
* @param comparator
* the comparator used to compare the line content string
*/
private LineComparator(Comparator<? super String> comparator) {
this.comparator = comparator;
public LineComparator(Comparator<? super String> comparator) {
this.comparator = Objects.requireNonNull(comparator);
}
/**
* Constructor with options for line comparision.
*
* @param ignoreCase true to ignore case, false for case sensitive comparison
* @param ignoreLeadingBlanks true if leading blanks shall be trimmed before the comparison
* @param onlyDictionaryChars true if only blanks and alphanumeric characters shall be considered
*/
public LineComparator(boolean ignoreCase, boolean ignoreLeadingBlanks, boolean onlyDictionaryChars) {
this(getCollator(ignoreCase, ignoreLeadingBlanks, onlyDictionaryChars));
}
@Override
public int compare(Line line1, Line line2) {
return comparator.compare(line1.getContent(), line2.getContent());
}
/**
* Line comparator using case sensitive comparison based on the current
* local's collation order.
*/
public static final LineComparator COLLATOR = new LineComparator(getCollator(false, false));
/**
* Line comparator using case insensitive comparison based on the current
* local's collation order.
*/
public static final LineComparator COLLATOR_IGNORE_CASE = new LineComparator(getCollator(true, false));
/**
* Line comparator using case sensitive comparison based on the current
* local's collation order, ignoring leading blanks (spaces and tabs) in the
* content string of the line.
*/
public static final LineComparator COLLATOR_IGNORE_LEADING_BLANKS = new LineComparator(getCollator(false, true));
/**
* Line comparator using case insensitive comparison based on the current
* local's collation order, ignoring leading blanks (spaces and tabs) in the
* content string of the line.
*/
public static final LineComparator COLLATOR_IGNORE_CASE_AND_LEADING_BLANKS = new LineComparator(getCollator(true, true));
private static Comparator<? super String> getCollator(boolean ignoreCase, boolean ignoreLeadingBlanks) {
private static Comparator<? super String> getCollator(boolean ignoreCase, boolean ignoreLeadingBlanks, boolean onlyDictionaryChars) {
final Collator collator = Collator.getInstance();
if (ignoreCase) {
collator.setStrength(Collator.SECONDARY);
}
collator.setStrength(ignoreCase ? Collator.SECONDARY : Math.max(Collator.TERTIARY, collator.getStrength()));
final Comparator<? super String> comparator;
if (ignoreLeadingBlanks) {
return new TrimBlanksStringComparator(Mode.Leading, collator);
comparator = new TrimBlanksStringComparator(Mode.Leading, collator);
} else {
comparator = collator;
}
if (onlyDictionaryChars) {
return new DictionaryStringComparator(comparator);
}
return collator;
return comparator;
};
}
@@ -0,0 +1,105 @@
package org.unix4j.util.sort;
import org.unix4j.util.StringUtil;
import java.text.DateFormatSymbols;
import java.util.Comparator;
import java.util.Locale;
/**
* A comparator Months: (unknown) < 'JAN' < ... < 'DEC'. The current locale
* determines the month spellings.
*/
public class MonthStringComparator implements Comparator<CharSequence> {
/**
* The instance for the default locale returned by {@link #getInstance()}.
*/
private static final MonthStringComparator DEFAULT_INSTANCE = new MonthStringComparator();
/**
* Returns the instance for the default locale.
*
* @see Locale#getDefault()
*/
public static MonthStringComparator getInstance() {
return DEFAULT_INSTANCE;
}
/**
* Returns an instance for the specified locale.
*/
public static MonthStringComparator getInstance(Locale locale) {
return new MonthStringComparator(locale);
}
private final String[] months;
private final String[] shortMonths;
/**
* Private constructor used to create the {@link #DEFAULT_INSTANCE}.
*/
private MonthStringComparator() {
this(DateFormatSymbols.getInstance());
}
/**
* Private constructor used by {@link #getInstance(Locale)}.
*/
private MonthStringComparator(Locale locale) {
this(DateFormatSymbols.getInstance(locale));
}
/**
* Constructor with date symbols.
*
* @param symbols
* the date symbols
*/
public MonthStringComparator(DateFormatSymbols symbols) {
this.months = symbols.getMonths();
this.shortMonths = symbols.getShortMonths();
}
@Override
public int compare(CharSequence s1, CharSequence s2) {
final int start1 = StringUtil.findStartTrimWhitespace(s1);
final int end1 = StringUtil.findEndTrimWhitespace(s1);
final int start2 = StringUtil.findStartTrimWhitespace(s2);
final int end2 = StringUtil.findEndTrimWhitespace(s2);
final int month1 = month(s1, start1, end1);
final int month2 = month(s2, start2, end2);
return Integer.compare(month1, month2);
}
private final int month(CharSequence s, int start, int end) {
int m = month(months, s, start, end);
return m >= 0 ? m : month(shortMonths, s, start, end);
}
private static int month(String[] months, CharSequence s, int start, int end) {
for (int i = 0; i < months.length; i++) {
final String month = months[i];
if (!equalsIgnoreCase(month, s, start, end)) {
continue;
}
return i;
}
return -1;
}
private static final boolean equalsIgnoreCase(String s1, CharSequence s2, int start, int end) {
if (end - start != s1.length()) {
return false;
}
for (int i = start; i < end; i++) {
if (!equalsIgnoreCase(s1.charAt(i - start), s2.charAt(i))) {
return false;
}
}
return true;
}
private static final boolean equalsIgnoreCase(char ch1, char ch2) {
return Character.toUpperCase(ch1) == Character.toUpperCase(ch2);
}
}
@@ -0,0 +1,104 @@
package org.unix4j.util.sort;
import org.unix4j.util.StringUtil;
import java.util.Comparator;
import java.util.Locale;
import java.util.Objects;
/**
* A comparator for decimal strings sorted numerically, first by numeric sign; then by SI suffix
* (either empty, or 'k' or 'K', or one of 'MGTPEZY', in that order); and finally by numeric value.
* For example, '1023M' sorts before '1G' because 'M' (mega) precedes 'G' (giga) as an SI suffix.
* <p>
* The syntax for numbers is the same as for {@link DecimalNumberStringComparator}; the SI suffix must
* immediately follow the number.
*/
public class UnitsNumberStringComparator implements Comparator<CharSequence> {
private static final String SI_UNITS = "KMGTPEZY";
/**
* The instance for the default locale returned by {@link #getInstance()}.
*/
private static final UnitsNumberStringComparator DEFAULT_INSTANCE = new UnitsNumberStringComparator();
/**
* Returns the instance for the default locale.
*
* @see Locale#getDefault()
*/
public static UnitsNumberStringComparator getInstance() {
return DEFAULT_INSTANCE;
}
/**
* Returns an instance for the specified locale.
*/
public static UnitsNumberStringComparator getInstance(Locale locale) {
return new UnitsNumberStringComparator(locale);
}
private final DecimalNumberStringComparator numberComparator;
/**
* Private constructor used to create the {@link #DEFAULT_INSTANCE}.
*/
private UnitsNumberStringComparator() {
this(DecimalNumberStringComparator.getInstance());
}
/**
* Private constructor used by {@link #getInstance(Locale)}.
*/
private UnitsNumberStringComparator(Locale locale) {
this(DecimalNumberStringComparator.getInstance(locale));
}
/**
* Constructor with decimal number comparator.
*
* @param numberComparator
* the decimal number comparator
*/
public UnitsNumberStringComparator(DecimalNumberStringComparator numberComparator) {
this.numberComparator = Objects.requireNonNull(numberComparator);
}
@Override
public int compare(CharSequence s1, CharSequence s2) {
final int start1 = StringUtil.findStartTrimWhitespace(s1);
final int end1 = StringUtil.findEndTrimWhitespace(s1);
final int start2 = StringUtil.findStartTrimWhitespace(s2);
final int end2 = StringUtil.findEndTrimWhitespace(s2);
final boolean isNeg1 = start1 < end1 && s1.charAt(start1) == '-';
final boolean isNeg2 = start2 < end2 && s2.charAt(start2) == '-';
final int si1 = siUnit(s1, start1, end1);
final int si2 = siUnit(s2, start2, end2);
final int e1 = si1 < 0 ? end1 : end1 - 1;
final int e2 = si2 < 0 ? end2 : end2 - 1;
final int cmp = numberComparator.compare(s1, start1, e1, s2, start2, e2);
if (isNeg1 != isNeg2) {
return cmp;
}
if (si1 < si2) {
return isNeg1 ? 1 : -1;
}
if (si1 > si2) {
return isNeg1 ? -1 : 1;
}
return cmp;
}
private static final int siUnit(CharSequence s, int start, int end) {
if (start < end) {
final int ch = s.charAt(end - 1);
final int si = SI_UNITS.indexOf(ch);
if (si >= 0) {
return si;
}
if (ch == 'k') return 0;//lower case only for k
}
return -1;
}
}
Oops, something went wrong.

0 comments on commit 0ab496e

Please sign in to comment.