001package org.unix4j.util.sort; 002 003import java.util.Comparator; 004 005import org.unix4j.util.StringUtil; 006 007/** 008 * Comparator for strings or character sequences trimming leading or trailing 009 * blanks or both before forwarding the actual comparison to a delegate 010 * comparator. 011 */ 012public class TrimBlanksStringComparator implements Comparator<String> { 013 014 /** 015 * Mode defining which end of the strings should be trimmed for blanks. 016 */ 017 public static enum Mode { 018 /** Only leading blanks are trimmed */ 019 Leading, 020 /** Only trailing blanks are trimmed */ 021 Trailing, 022 /** Both leading and trailing blanks are trimmed */ 023 Both 024 } 025 026 private final Mode mode; 027 private final Comparator<? super CharSequence> comparator; 028 029 /** 030 * Constructor with mode and delegate comparator performing the actual 031 * string comparison on the trimmed strings or character sequences. 032 * 033 * @param mode 034 * the mode defining which end of the character sequence should 035 * be trimmed 036 * @param comparator 037 * delegate comparator performing the actual comparison after 038 * trimming 039 */ 040 public TrimBlanksStringComparator(Mode mode, Comparator<? super CharSequence> comparator) { 041 this.mode = mode; 042 this.comparator = comparator; 043 } 044 045 @Override 046 public int compare(String s1, String s2) { 047 final int start1 = findStart(s1); 048 final int end1 = findEnd(s1); 049 final int start2 = findStart(s2); 050 final int end2 = findEnd(s2); 051 return comparator.compare(s1.subSequence(start1, end1), s2.subSequence(start2, end2)); 052 } 053 054 private int findStart(String s) { 055 if (mode == Mode.Trailing) 056 return 0; 057 return findStartTrimBlanks(s); 058 } 059 060 /** 061 * Finds and returns the start of the given character sequence after 062 * trimming spaces and tabs. 063 * 064 * @param s 065 * the character sequence 066 * @return the index containing the first non-blank character, or the length 067 * of the character sequence if all characters are blank 068 * @see StringUtil#findStartTrimWhitespace(CharSequence) 069 */ 070 static int findStartTrimBlanks(CharSequence s) { 071 final int len = s.length(); 072 for (int i = 0; i < len; i++) { 073 final char ch = s.charAt(i); 074 if (ch != ' ' && ch != '\t') { 075 return i; 076 } 077 } 078 return len; 079 } 080 081 private int findEnd(String s) { 082 if (mode == Mode.Leading) 083 return s.length(); 084 return findEndTrimBlanks(s); 085 } 086 087 /** 088 * Finds and returns the end of the given character sequence after trimming 089 * spaces and tabs. 090 * 091 * @param s 092 * the character sequence 093 * @return the index after the last non-blank character, or zero if all 094 * characters are blank 095 * @see StringUtil#findEndTrimWhitespace(CharSequence) 096 */ 097 static int findEndTrimBlanks(CharSequence s) { 098 for (int i = s.length(); i > 0; i--) { 099 final char ch = s.charAt(i - 1); 100 if (ch != ' ' && ch != '\t') { 101 return i; 102 } 103 } 104 return 0; 105 } 106}