001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import java.io.Serializable;
018    
019    import java.util.Comparator;
020    
021    /**
022     * @author Hugo Huijser
023     */
024    public class NaturalOrderStringComparator
025            implements Comparator<String>, Serializable {
026    
027            public NaturalOrderStringComparator() {
028                    this(true, false);
029            }
030    
031            public NaturalOrderStringComparator(
032                    boolean ascending, boolean caseSensitive) {
033    
034                    _ascending = ascending;
035                    _caseSensitive = caseSensitive;
036            }
037    
038            @Override
039            public int compare(String s1, String s2) {
040                    if (s1 == null) {
041                            s1 = StringPool.BLANK;
042                    }
043    
044                    if (s2 == null) {
045                            s2 = StringPool.BLANK;
046                    }
047    
048                    int value = 0;
049    
050                    int i1 = 0;
051                    int i2 = 0;
052    
053                    int length1 = s1.length();
054                    int length2 = s2.length();
055    
056                    while ((i1 < length1) && (i2 < length2)) {
057                            char c1 = s1.charAt(i1);
058                            char c2 = s2.charAt(i2);
059    
060                            if (Validator.isDigit(c1) && Validator.isDigit(c2)) {
061                                    String leadingDigitsAsString1 = StringUtil.extractLeadingDigits(
062                                            s1.substring(i1));
063                                    String leadingDigitsAsString2 = StringUtil.extractLeadingDigits(
064                                            s2.substring(i2));
065    
066                                    int leadingNumber1 = GetterUtil.getInteger(
067                                            leadingDigitsAsString1);
068                                    int leadingNumber2 = GetterUtil.getInteger(
069                                            leadingDigitsAsString2);
070    
071                                    if (leadingNumber1 != leadingNumber2) {
072                                            value = leadingNumber1 - leadingNumber2;
073    
074                                            break;
075                                    }
076    
077                                    i1 += leadingDigitsAsString1.length();
078                                    i2 += leadingDigitsAsString2.length();
079    
080                                    continue;
081                            }
082    
083                            if (isCheckSpecialCharacters() && Validator.isAscii(c1) &&
084                                    Validator.isAscii(c2)) {
085    
086                                    boolean isDigitOrLetter1 = _isDigitOrLetter(c1);
087                                    boolean isDigitOrLetter2 = _isDigitOrLetter(c2);
088    
089                                    if (isDigitOrLetter1 ^ isDigitOrLetter2) {
090                                            if (isDigitOrLetter1) {
091                                                    value = 1;
092                                            }
093                                            else {
094                                                    value = -1;
095                                            }
096    
097                                            break;
098                                    }
099                            }
100    
101                            if (c1 == c2) {
102                                    i1++;
103                                    i2++;
104    
105                                    continue;
106                            }
107    
108                            if (_caseSensitive) {
109                                    value = c1 - c2;
110    
111                                    break;
112                            }
113                            else {
114                                    char c1UpperCase = Character.toUpperCase(c1);
115                                    char c2UpperCase = Character.toUpperCase(c2);
116    
117                                    if (c1UpperCase == c2UpperCase) {
118                                            i1++;
119                                            i2++;
120    
121                                            continue;
122                                    }
123    
124                                    value = c1UpperCase - c2UpperCase;
125    
126                                    break;
127                            }
128                    }
129    
130                    if ((value == 0) && (length1 != length2)) {
131                            if ((length1 == i1) && (length2 == i2)) {
132                                    value = length2 - length1;
133                            }
134                            else {
135                                    value = length1 - length2;
136                            }
137                    }
138    
139                    if (_ascending) {
140                            return value;
141                    }
142                    else {
143                            return -value;
144                    }
145            }
146    
147            protected boolean isCheckSpecialCharacters() {
148                    return true;
149            }
150    
151            private boolean _isDigitOrLetter(char c) {
152                    if (Validator.isChar(c) || Validator.isDigit(c)) {
153                            return true;
154                    }
155                    else {
156                            return false;
157                    }
158            }
159    
160            private final boolean _ascending;
161            private final boolean _caseSensitive;
162    
163    }