EMMA Coverage Report (generated Sun Apr 20 22:38:01 CEST 2008)
[all classes][net.sf.jomic.tools]

COVERAGE SUMMARY FOR SOURCE FILE [NaturalOrderComparator.java]

nameclass, %method, %block, %line, %
NaturalOrderComparator.java100% (1/1)100% (7/7)93%  (423/453)98%  (98.5/101)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class NaturalOrderComparator100% (1/1)100% (7/7)93%  (423/453)98%  (98.5/101)
NaturalOrderComparator (): void 100% (1/1)64%  (9/14)88%  (2.6/3)
<static initializer> 100% (1/1)80%  (12/15)80%  (0.8/1)
extractNumber (String, int): StringBuffer 100% (1/1)91%  (81/89)94%  (15.1/16)
compare (Object, Object): int 100% (1/1)95%  (270/284)98%  (63/64)
sgn (int): int 100% (1/1)100% (14/14)100% (6/6)
stripLeadingZeros (StringBuffer): void 100% (1/1)100% (21/21)100% (5/5)
tokenOf (char): char 100% (1/1)100% (16/16)100% (6/6)

1// Jomic - a viewer for comic book archives.
2// Copyright (C) 2004-2008 Thomas Aglassinger
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16package net.sf.jomic.tools;
17 
18import java.util.Comparator;
19 
20import net.sf.wraplog.Logger;
21 
22/**
23 *  Comparator to perform a 'natural order' comparisons of strings.
24 *
25 * @author    Thomas Aglassinger
26 */
27public class NaturalOrderComparator implements Comparator
28{
29    private static final int DEFAULT_NUMBER_LENGTH = 16;
30    private static final char TOKEN_DIGIT = 'd';
31    private static final char TOKEN_OTHER = 'o';
32    private static final char TOKEN_SPACE = 's';
33 
34    private Logger logger;
35 
36    public NaturalOrderComparator() {
37        logger = Logger.getLogger(NaturalOrderComparator.class);
38    }
39 
40    public int compare(Object o1, Object o2) {
41        int result = Integer.MAX_VALUE;
42        String a = o1.toString();
43        String b = o2.toString();
44        int ia = 0;
45        int ib = 0;
46        int aLength = a.length();
47        int bLength = b.length();
48 
49        // skip leading white space
50        while ((ia < aLength) && Character.isWhitespace(a.charAt(ia))) {
51            ia += 1;
52        }
53        while ((ib < bLength) && Character.isWhitespace(b.charAt(ib))) {
54            ib += 1;
55        }
56 
57        while (result == Integer.MAX_VALUE) {
58            // did one of the strings reach its end?
59            if (ia == aLength) {
60                if (ib == bLength) {
61                    // both strings reached their end
62                    result = 0;
63                } else {
64                    // a reached its end, b goes on
65                    result = -1;
66                }
67            } else if (ib == bLength) {
68                // b reached its end, a goes on
69                result = 1;
70            } else {
71                char aChar = a.charAt(ia);
72                char aType = tokenOf(aChar);
73                char bChar = b.charAt(ib);
74                char bType = tokenOf(bChar);
75 
76                if (aType != bType) {
77                    result = sgn(aChar - bChar);
78                } else if (aType == TOKEN_SPACE) {
79                    // seek until end of space
80                    while (((ia + 1) < aLength)
81                            && (tokenOf(a.charAt(ia + 1)) == TOKEN_SPACE)) {
82                        ia += 1;
83                    }
84                    while (((ib + 1) < bLength)
85                            && (tokenOf(b.charAt(ib + 1)) == TOKEN_SPACE)) {
86                        ib += 1;
87                    }
88                } else if (aType == TOKEN_DIGIT) {
89                    StringBuffer aNumber = extractNumber(a, ia);
90 
91                    if (logger.isDebugEnabled()) {
92                        logger.debug("aNumber=" + aNumber);
93                    }
94                    StringBuffer bNumber = extractNumber(b, ib);
95 
96                    if (logger.isDebugEnabled()) {
97                        logger.debug("bNumber=" + bNumber);
98                    }
99                    ia += aNumber.length() - 1;
100                    ib += bNumber.length() - 1;
101                    stripLeadingZeros(aNumber);
102                    if (logger.isDebugEnabled()) {
103                        logger.debug("aNumber\\0=" + aNumber);
104                    }
105                    stripLeadingZeros(bNumber);
106                    if (logger.isDebugEnabled()) {
107                        logger.debug("bNumber\\0=" + bNumber);
108                    }
109                    int deltaLength = sgn(aNumber.length() - bNumber.length());
110 
111                    if (deltaLength == 0) {
112                        // numbers have same length; compare them digit by digit
113                        int i = 0;
114                        int deltaDigit;
115 
116                        do {
117                            deltaDigit = aNumber.charAt(i) - bNumber.charAt(i);
118                            if (deltaDigit == 0) {
119                                i += 1;
120                            } else {
121                                // digits differ
122                                result = sgn(deltaDigit);
123                            }
124                        } while ((deltaDigit == 0) && (i < aNumber.length()));
125                    } else {
126                        // numbers differ in length
127                        result = deltaLength;
128                    }
129                } else if (aType == TOKEN_OTHER) {
130                    int cmp = sgn(aChar - bChar);
131 
132                    if (cmp != 0) {
133                        result = cmp;
134                    }
135                } else {
136                    assert false : "type = " + aType;
137                }
138                ia += 1;
139                ib += 1;
140            }
141        }
142        return result;
143    }
144 
145    private StringBuffer extractNumber(String some, int startIndex) {
146        assert some != null;
147        assert startIndex < some.length();
148        StringBuffer result = new StringBuffer(DEFAULT_NUMBER_LENGTH);
149        int index = startIndex;
150        boolean foundNonDigit = false;
151 
152        if (logger.isDebugEnabled()) {
153            logger.debug("extractNumber(\"" + some + "\", " + startIndex + ")");
154        }
155        do {
156            char c = some.charAt(index);
157 
158            if (logger.isDebugEnabled()) {
159                logger.debug("  " + c);
160            }
161            foundNonDigit = !Character.isDigit(c);
162            if (!foundNonDigit) {
163                result.append(c);
164                index += 1;
165            }
166        } while (!foundNonDigit && (index < some.length()));
167        return result;
168    }
169 
170    private int sgn(int x) {
171        int result;
172 
173        if (x > 0) {
174            result = 1;
175        } else if (x == 0) {
176            result = 0;
177        } else {
178            result = -1;
179        }
180        return result;
181    }
182 
183    private void stripLeadingZeros(StringBuffer some) {
184        int deleteCount = 0;
185 
186        while ((deleteCount < some.length() - 1)
187                && (some.charAt(deleteCount) == '0')) {
188            deleteCount += 1;
189        }
190        some.delete(0, deleteCount);
191    }
192 
193    private char tokenOf(char c) {
194        char result;
195 
196        if (Character.isDigit(c)) {
197            result = TOKEN_DIGIT;
198        } else if (Character.isWhitespace(c)) {
199            result = TOKEN_SPACE;
200        } else {
201            result = TOKEN_OTHER;
202        }
203        return result;
204    }
205}

[all classes][net.sf.jomic.tools]
EMMA 2.0.4217 (C) Vladimir Roubtsov