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}