Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.utils / src / main / java / org / gvsig / utils / search / BinarySearchUsingFirstCharacters.java @ 40561

History | View | Annotate | Download (16.1 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.utils.search;
25

    
26
import java.util.ArrayList;
27
import java.util.Arrays;
28
import java.util.Comparator;
29
import java.util.List;
30
import java.util.Vector;
31

    
32
import org.gvsig.utils.MathExtension;
33

    
34

    
35
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
36
 *
37
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
38
 *
39
 * This program is free software; you can redistribute it and/or
40
 * modify it under the terms of the GNU General Public License
41
 * as published by the Free Software Foundation; either version 2
42
 * of the License, or (at your option) any later version.
43
 *
44
 * This program is distributed in the hope that it will be useful,
45
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
46
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
47
 * GNU General Public License for more details.
48
 *
49
 * You should have received a copy of the GNU General Public License
50
 * along with this program; if not, write to the Free Software
51
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
52
 *
53
 * For more information, contact:
54
 *
55
 *  Generalitat Valenciana
56
 *   Conselleria d'Infraestructures i Transport
57
 *   Av. Blasco Ib??ez, 50
58
 *   46010 VALENCIA
59
 *   SPAIN
60
 *
61
 *      +34 963862235
62
 *   gvsig@gva.es
63
 *      www.gvsig.gva.es
64
 *
65
 *    or
66
 *
67
 *   IVER T.I. S.A
68
 *   Salamanca 50
69
 *   46005 Valencia
70
 *   Spain
71
 *
72
 *   +34 963163400
73
 *   dac@iver.es
74
 */
75

    
76
/**
77
 * This class has static methods that return items that their beginning text value matches with a text pattern. <br>
78
 * It's necessary that items of the parameter array (Vector) were sort ordered according to their text value. <br>
79
 * Supports Vectors with and without repeated items.
80
 * 
81
 * There are four methods, that are a modification of a binary search algorithm of search, getting a rank of items:
82
 * <ul>
83
 * <li>Considering case sensitive in the search.</li>
84
 * <li>Ignoring case sensitive in the search.</li>
85
 * <li>Considering case sensitive in the search and an object which implements the Comparable interface</li>
86
 * <li>Ignoring case sensitive in the search, but yes an objecth which implements the Comparable interface</li>
87
 * </ul>
88
 *
89
 * @author Pablo Piqueras Bartolom? (pablo.piqueras@iver.es)
90
 */
91
public class BinarySearchUsingFirstCharacters {
92
        /**
93
         * This method should be used when is wanted to distinguish small letters from capital letters during the search.
94
         *
95
         * It's necessary that all items of the array implement the {@link Comparable} interface.<br>
96
         * And it's also necessary that the value returned by the <i>toString()</i> method of each item (supposing
97
         *   they inherit from Object) would be the expected value user saw (that would be used to compare the items).
98
         *
99
         * @param text java.lang.String
100
         * @param sortOrderedItems java.util.Vector
101
         */
102
        public synchronized static List doSearchConsideringCaseSensitive(String text, Vector sortOrderedItems) {
103
                int currentIteration = 0;
104
                int size = sortOrderedItems.size();
105
                int maxNumberOfIterations = (int) MathExtension.log2(size);
106
                int lowIndex = 0;
107
                int highIndex = sortOrderedItems.size() - 1;
108
                int mIndx;
109

    
110
                while ((lowIndex <= highIndex) && (currentIteration <= maxNumberOfIterations)) {
111
                        mIndx = ( lowIndex + highIndex ) / 2;
112

    
113
                        if ( sortOrderedItems.get( mIndx ).toString().startsWith( text ) ) {
114
                                lowIndex = highIndex = mIndx;
115
                                highIndex ++;
116

    
117
                                // Expand the rank to up
118
                                while ( ( highIndex < size ) && ( sortOrderedItems.get( highIndex ).toString().startsWith( text ) ) ) {
119
                                        highIndex ++;
120
                                }
121

    
122
                                // Expand the rank to down
123
                                while ( ( (lowIndex - 1) > -1 ) && ( sortOrderedItems.get( (lowIndex - 1) ).toString().startsWith( text ) ) ) {
124
                                        lowIndex --;
125
                                }
126

    
127
                                // It's possible that items with different case, should be between the same case, then this item will be added individually:
128
                                List list = new Vector(sortOrderedItems.subList(lowIndex, highIndex));
129

    
130
                                // Expand to down
131
                                lowIndex --;
132
                                while ( ( lowIndex > -1 ) && ( sortOrderedItems.get( (lowIndex ) ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
133
                                        if (sortOrderedItems.get( lowIndex ).toString().startsWith( text )) {
134
                                                list.add(0, sortOrderedItems.get( lowIndex ));
135
                                        }
136

    
137
                                        lowIndex --;
138
                                }
139

    
140
                                // Expand to up
141
                                while ( ( highIndex < size ) && ( sortOrderedItems.get( highIndex ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
142
                                        if (sortOrderedItems.get( highIndex ).toString().startsWith( text )) {
143
                                                list.add(list.size(), sortOrderedItems.get( highIndex ));
144
                                        }
145

    
146
                                        highIndex ++;
147
                                }
148

    
149
                                // Returns all items in the rank
150
                                return list; // Breaks the loop
151
                        }
152
                        else {
153
                                if ( sortOrderedItems.get( mIndx ).toString().compareTo( text ) > 0 ) {
154
                                        highIndex = mIndx - 1;
155
                                }
156
                                else {
157
                                        lowIndex = mIndx + 1;
158
                                }
159
                        }
160

    
161
                        currentIteration ++;
162
                }
163

    
164
                // If no item has been found -> return null
165
                return null;
166
        }
167

    
168
        /**
169
         * This method should be used when is wanted not to distinguish small letters from capital letters during the search.
170
         *
171
         * It's necessary that all items of the array implement the {@link Comparable} interface.<br>
172
         * And it's also necessary that the value returned by the <i>toString()</i> method of each item (supposing
173
         *   they inherit from Object) would be the expected value user saw (that would be used to compare the items).
174
         *
175
         * In this particular situation, it's supposed that the vector is sort ordered according the default algorithm of Java; this has the problem that
176
         *   it doesn't consideres the special characters and the orthographic rules of languages that aren't English, and then, for a particular
177
         *   <i>text</i> search, an incorrect result could be obtained. The solution decided for this problem has been to modify the algorithm, for seach
178
         *   into two ranks.
179
         *
180
         * @param text java.lang.String
181
         * @param sortOrderedItems java.util.Vector
182
         */
183
        public synchronized static List doSearchIgnoringCaseSensitive(String text, Vector sortOrderedItems) {
184
                int currentIteration = 0;
185
                int size = sortOrderedItems.size();
186
                int maxNumberOfIterations = (int) MathExtension.log2(size);
187
                int lowIndex = 0;
188
                int highIndex = sortOrderedItems.size() - 1;
189
                int mIndx;
190
                List list = null;
191
                List list2 = null;
192

    
193
                // FIRST RANK
194
                while ((lowIndex <= highIndex) && (currentIteration <= maxNumberOfIterations)) {
195
                        mIndx = ( lowIndex + highIndex ) / 2;
196

    
197
                        if ( sortOrderedItems.get( mIndx ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) {
198
                                lowIndex = highIndex = mIndx;
199
                                highIndex ++;
200

    
201
                                // Expand the rank to up
202
                                while ( ( highIndex < size ) && ( sortOrderedItems.get( highIndex ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
203
                                        highIndex ++;
204
                                }
205

    
206
                                // Expand the rank to down
207
                                while ( ( (lowIndex - 1) > -1 ) && ( sortOrderedItems.get( (lowIndex - 1) ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
208
                                        lowIndex --;
209
                                }
210

    
211
                                // Returns all items in the rank
212
                                list = Arrays.asList((sortOrderedItems.subList(lowIndex, highIndex)).toArray());
213
                                break;
214
                        }
215
                        else {
216
                                if ( sortOrderedItems.get( mIndx ).toString().compareTo( text ) > 0 ) {
217
                                        highIndex = mIndx - 1;
218
                                }
219
                                else {
220
                                        lowIndex = mIndx + 1;
221
                                }
222
                        }
223

    
224
                        currentIteration ++;
225
                }
226

    
227
                // SECOND RANK
228
                currentIteration = 0;
229
                lowIndex = 0;
230
                highIndex = sortOrderedItems.size() - 1;
231

    
232
                while ((lowIndex <= highIndex) && (currentIteration <= maxNumberOfIterations)) {
233
                        mIndx = ( lowIndex + highIndex ) / 2;
234

    
235
                        if ( sortOrderedItems.get( mIndx ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) {
236
                                lowIndex = highIndex = mIndx;
237
                                highIndex ++;
238

    
239
                                // Expand the rank to up
240
                                while ( ( highIndex < size ) && ( sortOrderedItems.get( highIndex ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
241
                                        highIndex ++;
242
                                }
243

    
244
                                // Expand the rank to down
245
                                while ( ( (lowIndex - 1) > -1 ) && ( sortOrderedItems.get( (lowIndex - 1) ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
246
                                        lowIndex --;
247
                                }
248

    
249
                                // Returns all items in the rank
250
                                list2 = Arrays.asList((sortOrderedItems.subList(lowIndex, highIndex)).toArray()); // Breaks the loop
251

    
252
                                if (list == null)
253
                                        return list2;
254
                                else {
255
                                        if (list2 == null)
256
                                                return null;
257

    
258
                                        Object obj;
259
                                        int j;
260

    
261
                                        list = new ArrayList(list.subList(0, list.size()));
262

    
263
                                        for (int i = 0; i < list2.size(); i ++) {
264
                                                obj = list2.get(i);
265

    
266
                                                // Don't add items which are already in the list
267
                                                if (!list.contains(obj)) {
268
                                                        // Adds in sort order the new item:
269
                                                        for (j = 0; j < list.size(); j ++) {
270
                                                                if (list.get(j).toString().compareTo(obj.toString()) > 0)
271
                                                                        break;
272
                                                        }
273

    
274
                                                        list.add(j, obj);
275
                                                }
276
                                        }
277

    
278
                                        // It's possible that some elements at the end wouldn't be found -> another small search
279
                                        size = list.size();
280
                                        if (size == 0) {
281
                                                j = 0;
282
                                        }
283
                                        else {
284
                                                j = sortOrderedItems.indexOf(list.get(size - 1));
285
                                        }
286

    
287
                                        j++;
288

    
289
                                        if (j < sortOrderedItems.size()) {
290
                                                do {
291
                                                        obj = sortOrderedItems.get( j );
292

    
293
                                                        if (obj.toString().toLowerCase().startsWith( text.toLowerCase())) {
294
                                                                list.add(size, obj);
295
                                                        }
296

    
297
                                                        j++;
298
                                                }
299
                                                while (j < sortOrderedItems.size());
300
                                        }
301

    
302
                                        return list;
303
                                }
304
                        }
305
                        else {
306
                                if ( sortOrderedItems.get( mIndx ).toString().toLowerCase().compareTo( text.toLowerCase() ) > 0 ) {
307
                                        highIndex = mIndx - 1;
308
                                }
309
                                else {
310
                                        lowIndex = mIndx + 1;
311
                                }
312
                        }
313

    
314
                        currentIteration ++;
315
                }
316

    
317
                return null;
318
        }
319

    
320
//        /** THIS VERSION FAILURES IN SOME PARTICULAR SITUATIONS
321
//         * This method should be used when is wanted distinguish small letters from capital letters during the search, and the comparation of items
322
//         *   done according an algorithm we define.
323
//         *
324
//         * And it's also necessary that the value returned by the <i>toString()</i> method of each item (supposing
325
//         *   they inherit from Object) would be the expected value user saw (that would be used to compare the items).
326
//         *
327
//         * @param text java.lang.String
328
//         * @param sortOrderedItems java.util.Vector
329
//         * @param comp An Comparator object which implements the <i><b>compareTo()</b><i>  method.
330
//         */
331
//        public synchronized static List doSearchConsideringCaseSensitive(String text, Vector sortOrderedItems, Comparator comp) {
332
//                int currentIteration = 0;
333
//                int size = sortOrderedItems.size();
334
//                int maxNumberOfIterations = (int) MathExtension.log2(size);
335
//                int lowIndex = 0;
336
//                int highIndex = sortOrderedItems.size() - 1;
337
//                int mIndx;
338
//
339
//
340
//                while ((lowIndex <= highIndex) && (currentIteration <= maxNumberOfIterations)) {
341
//                        mIndx = ( lowIndex + highIndex ) / 2;
342
//
343
//                        if ( sortOrderedItems.get( mIndx ).toString().startsWith( text ) ) {
344
//                                lowIndex = highIndex = mIndx;
345
//                                highIndex ++;
346
//
347
//                                // Expand the rank to up
348
//                                while ( ( highIndex < size ) && ( sortOrderedItems.get( highIndex ).toString().startsWith( text ) ) ) {
349
//                                        highIndex ++;
350
//                                }
351
//
352
//                                // Expand the rank to down
353
//                                while ( ( (lowIndex - 1) > -1 ) && ( sortOrderedItems.get( (lowIndex - 1) ).toString().startsWith( text ) ) ) {
354
//                                        lowIndex --;
355
//                                }
356
//
357
//                                // It's possible that items with different case, should be between the same case, then this item will be added individually:
358
//                                List list = new Vector(sortOrderedItems.subList(lowIndex, highIndex));
359
//
360
//
361
//                                // Expand to down
362
//                                lowIndex --;
363
//                                while ( ( lowIndex > -1 ) && ( sortOrderedItems.get( (lowIndex ) ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
364
//                                        if (sortOrderedItems.get( lowIndex ).toString().startsWith( text )) {
365
//                                                list.add(0, sortOrderedItems.get( lowIndex ));
366
//                                        }
367
//
368
//                                        lowIndex --;
369
//                                }
370
//
371
//                                // Expand to up
372
//                                while ( ( highIndex < size ) && ( sortOrderedItems.get( highIndex ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
373
//                                        if (sortOrderedItems.get( highIndex ).toString().startsWith( text )) {
374
//                                                list.add(list.size(), sortOrderedItems.get( highIndex ));
375
//                                        }
376
//
377
//                                        highIndex ++;
378
//                                }
379
//
380
//                                // Returns all items in the rank
381
//                                return list; // Breaks the loop
382
//                        }
383
//                        else {
384
//                                if ( comp.compare(sortOrderedItems.get( mIndx ), text ) > 0 ) {
385
//                                        highIndex = mIndx - 1;
386
//                                }
387
//                                else {
388
//                                        lowIndex = mIndx + 1;
389
//                                }
390
//                        }
391
//
392
//                        currentIteration ++;
393
//                }
394
//
395
//                // If no item has been found -> return null
396
//                return null;
397
//        }
398

    
399
        /**
400
         * This method should be used when is wanted distinguish small letters from capital letters during the search, and the comparation of items
401
         *   done according an algorithm we define.
402
         *
403
         * And it's also necessary that the value returned by the <i>toString()</i> method of each item (supposing
404
         *   they inherit from Object) would be the expected value user saw (that would be used to compare the items).
405
         *
406
         * @param text java.lang.String
407
         * @param sortOrderedItems java.util.Vector
408
         * @param comp An Comparator object which implements the <i><b>compareTo()</b><i>  method.
409
         */
410
        public synchronized static List doSearchConsideringCaseSensitive(String text, Vector sortOrderedItems, Comparator comp) {
411
                List results_list = doSearchIgnoringCaseSensitive(text, sortOrderedItems, comp);
412

    
413
                if (results_list == null)
414
                        return null;
415

    
416
                List results = new ArrayList();
417

    
418
                for (int i = 0; i < (results_list.size()); i++) {
419
                        if (results_list.get(i).toString().startsWith(text)) {
420
                                results.add(results_list.get(i));
421
                        }
422
                }
423

    
424
                return results;
425
        }
426

    
427
        /**
428
         * This method should be used when is wanted not to distinguish small letters from capital letters during the search, and the comparation of items
429
         *   done according an algorithm we define.
430
         *
431
         * And it's also necessary that the value returned by the <i>toString()</i> method of each item (supposing
432
         *   they inherit from Object) would be the expected value user saw (that would be used to compare the items).
433
         *
434
         * @param text java.lang.String
435
         * @param sortOrderedItems java.util.Vector
436
         * @param comp An Comparator object which implements the <i><b>compareTo()</b><i>  method.
437
         */
438
        public synchronized static List doSearchIgnoringCaseSensitive(String text, Vector sortOrderedItems, Comparator comp) {
439
                int currentIteration = 0;
440
                int size = sortOrderedItems.size();
441
                int maxNumberOfIterations = (int) MathExtension.log2(size);
442
                int lowIndex = 0;
443
                int highIndex = sortOrderedItems.size() - 1;
444
                int mIndx;
445

    
446
                while ((lowIndex <= highIndex) && (currentIteration <= maxNumberOfIterations)) {
447
                        mIndx = ( lowIndex + highIndex ) / 2;
448

    
449
                        if ( sortOrderedItems.get( mIndx ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) {
450
                                lowIndex = highIndex = mIndx;
451
                                highIndex ++;
452

    
453
                                // Expand the rank to up
454
                                while ( ( highIndex < size ) && ( sortOrderedItems.get( highIndex ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
455
                                        highIndex ++;
456
                                }
457

    
458
                                // Expand the rank to down
459
                                while ( ( (lowIndex - 1) > -1 ) && ( sortOrderedItems.get( (lowIndex - 1) ).toString().toLowerCase().startsWith( text.toLowerCase() ) ) ) {
460
                                        lowIndex --;
461
                                }
462

    
463
                                // Returns all items in the rank
464
                                return Arrays.asList((sortOrderedItems.subList(lowIndex, highIndex)).toArray()); // Breaks the loop
465
                        }
466
                        else {
467
                                if ( comp.compare(sortOrderedItems.get( mIndx ).toString().toLowerCase(), text.toLowerCase() ) > 0 ) {
468
                                        highIndex = mIndx - 1;
469
                                }
470
                                else {
471
                                        lowIndex = mIndx + 1;
472
                                }
473
                        }
474

    
475
                        currentIteration ++;
476
                }
477

    
478
                return null;
479
        }
480
}