1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2005 John Chapman. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        Initial release: 2005
8 
9         author:         John Chapman
10 
11 ******************************************************************************/
12 
13 module tango.text.locale.Collation;
14 
15 private import tango.text.locale.Core;
16 
17 version (Windows)
18   private import tango.text.locale.Win32;
19 else version (Posix)
20   private import tango.text.locale.Posix;
21 
22   /**
23   Compares strings using the specified case and cultural comparision rules.
24  */
25 public class StringComparer {
26 
27   private static StringComparer invariant_;
28   private static StringComparer invariantIgnoreCase_;
29   private Culture culture_;
30   private bool ignoreCase_;
31 
32   shared static this() {
33     invariant_ = new StringComparer(Culture.invariantCulture(), false);
34     invariantIgnoreCase_ = new StringComparer(Culture.invariantCulture(), true);
35   }
36 
37   /**
38     Creates an instance that compares strings using the rules of the specified culture.
39     Params:
40       culture = A Culture instance whose rules are used to compare strings.
41       ignoreCase = true to perform case-insensitive comparisons; false to perform case-sensitive comparisions.
42   */
43   public this(Culture culture, bool ignoreCase) {
44     culture_ = culture;
45     ignoreCase_ = ignoreCase;
46   }
47 
48   /**
49     Compares two strings and returns the sort order.
50     Returns:
51       -1 is strA is less than strB; 0 if strA is equal to strB; 1 if strA is greater than strB.
52     Params:
53       strA = A string to compare to strB.
54       strB = A string to compare to strA.
55   */
56   public int compare(const(char)[] strA, const(char)[] strB) {
57     return nativeMethods.compareString(culture_.id, strA, 0, strA.length, strB, 0, strB.length, ignoreCase_);
58   }
59 
60   /**
61     Indicates whether the two strings are equal.
62     Returns:
63       true if strA and strB are equal; otherwise, false.
64     Params:
65       strA = A string to compare to strB.
66       strB = A string to compare to strA.
67   */
68   public bool equals(const(char)[] strA, const(char)[] strB) {
69     return (compare(strA, strB) == 0);
70   }
71 
72   /**
73     $(I Property.) Retrieves an instance that performs case-sensitive comparisons using the rules of the current culture.
74     Returns:
75       A new StringComparer instance.
76   */
77   public static StringComparer currentCulture() {
78     return new StringComparer(Culture.current, false);
79   }
80 
81   /**
82     $(I Property.) Retrieves an instance that performs case-insensitive comparisons using the rules of the current culture.
83     Returns:
84       A new StringComparer instance.
85   */
86   public static StringComparer currentCultureIgnoreCase() {
87     return new StringComparer(Culture.current, true);
88   }
89 
90   /**
91     $(I Property.) Retrieves an instance that performs case-sensitive comparisons using the rules of the invariant culture.
92     Returns:
93       A new StringComparer instance.
94   */
95   public static StringComparer invariantCulture() {
96     return invariant_;
97   }
98 
99   /**
100     $(I Property.) Retrieves an instance that performs case-insensitive comparisons using the rules of the invariant culture.
101     Returns:
102       A new StringComparer instance.
103   */
104   public static StringComparer invariantCultureIgnoreCase() {
105     return invariantIgnoreCase_;
106   }
107 
108 }
109 
110 /**
111   $(I Delegate.) Represents the method that will handle the string comparison.
112   Remarks:
113     The delegate has the signature $(I int delegate(const(char)[], const(char)[])).
114  */
115 alias int delegate(const(char)[], const(char)[]) StringComparison;
116 
117 /**
118   Sorts strings according to the rules of the specified culture.
119  */
120 public class StringSorter {
121 
122   private static StringSorter invariant_;
123   private static StringSorter invariantIgnoreCase_;
124   private Culture culture_;
125   private StringComparison comparison_;
126 
127   shared static this() {
128     invariant_ = new StringSorter(StringComparer.invariantCulture());
129     invariantIgnoreCase_ = new StringSorter(StringComparer.invariantCultureIgnoreCase());
130   }
131 
132   /**
133     Creates an instance using the specified StringComparer.
134     Params:
135       comparer = The StringComparer to use when comparing strings. $(I Optional.)
136   */
137   public this(StringComparer comparer = null) {
138     if (comparer is null)
139       comparer = StringComparer.currentCulture();
140     comparison_ = &comparer.compare;
141   }
142 
143   /**
144     Creates an instance using the specified delegate.
145     Params:
146       comparison = The delegate to use when comparing strings.
147     Remarks:
148       The comparison parameter must have the same signature as StringComparison.
149   */
150   public this(StringComparison comparison) {
151     comparison_ = comparison;
152   }
153 
154   /**
155     Sorts all the elements in an array.
156     Params:
157       array = The array of strings to _sort.
158   */
159   public inout(void) sort(ref inout(char)[][] array) {
160     sort(array, 0, array.length);
161   }
162 
163   /**
164     Sorts a range of the elements in an array.
165     Params:
166       array = The array of strings to _sort.
167       index = The starting index of the range.
168       count = The number of elements in the range.
169   */
170   public inout(void) sort(ref inout(char)[][] array, size_t index, size_t count) {
171     inout(void) qsort(size_t left, size_t right, inout(int*) dummy = null) {
172       do {
173         size_t i = left, j = right;
174         const(char)[] pivot = array[left + ((right - left) >> 1)];
175 
176         do {
177           while (comparison_(array[i], pivot) < 0)
178             i++;
179           while (comparison_(pivot, array[j]) < 0)
180             j--;
181 
182           if (i > j)
183             break;
184           else if (i < j) {
185             auto temp = array[i];
186             array[i] = array[j];
187             array[j] = temp;
188           }
189 
190           i++;
191           j--;
192         } while (i <= j);
193 
194         if (j - left <= right - i) {
195           if (left < j)
196             qsort(left, j);
197           left = i;
198         }
199         else {
200           if (i < right)
201             qsort(i, right);
202           right = j;
203         }
204       } while (left < right);
205     }
206 
207     qsort(index, index + (count - 1));
208   }
209 
210   /**
211     $(I Property.) Retrieves an instance that performs a case-sensitive sort using the rules of the current culture.
212     Returns: A StringSorter instance.
213   */
214   @property public static StringSorter currentCulture() {
215     return new StringSorter(StringComparer.currentCulture());
216   }
217 
218   /**
219     $(I Property.) Retrieves an instance that performs a case-insensitive sort using the rules of the current culture.
220     Returns: A StringSorter instance.
221   */
222   @property public static StringSorter currentCultureIgnoreCase() {
223     return new StringSorter(StringComparer.currentCultureIgnoreCase());
224   }
225 
226   /**
227     $(I Property.) Retrieves an instance that performs a case-sensitive sort using the rules of the invariant culture.
228     Returns: A StringSorter instance.
229   */
230   public static StringSorter invariantCulture() {
231     return invariant_;
232   }
233 
234   /**
235     $(I Property.) Retrieves an instance that performs a case-insensitive sort using the rules of the invariant culture.
236     Returns: A StringSorter instance.
237   */
238   public static StringSorter invariantCultureIgnoreCase() {
239     return invariantIgnoreCase_;
240   }
241 
242 }