~slub.team/goobi-indexserver/3.x

« back to all changes in this revision

Viewing changes to lucene/contrib/analyzers/common/src/java/org/apache/lucene/analysis/ru/RussianStemmer.java

  • Committer: Sebastian Meyer
  • Date: 2012-08-03 09:12:40 UTC
  • Revision ID: sebastian.meyer@slub-dresden.de-20120803091240-x6861b0vabq1xror
Remove Lucene and Solr source code and add patches instead
Fix Bug #985487: Auto-suggestion for the search interface

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
package org.apache.lucene.analysis.ru;
2
 
 
3
 
/**
4
 
 * Licensed to the Apache Software Foundation (ASF) under one or more
5
 
 * contributor license agreements.  See the NOTICE file distributed with
6
 
 * this work for additional information regarding copyright ownership.
7
 
 * The ASF licenses this file to You under the Apache License, Version 2.0
8
 
 * (the "License"); you may not use this file except in compliance with
9
 
 * the License.  You may obtain a copy of the License at
10
 
 *
11
 
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 
 *
13
 
 * Unless required by applicable law or agreed to in writing, software
14
 
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 
 * See the License for the specific language governing permissions and
17
 
 * limitations under the License.
18
 
 */
19
 
 
20
 
/**
21
 
 * Russian stemming algorithm implementation (see http://snowball.sourceforge.net for detailed description).
22
 
 * @deprecated Use {@link org.tartarus.snowball.ext.RussianStemmer} instead, 
23
 
 * which has the same functionality. This filter will be removed in Lucene 4.0
24
 
 */
25
 
@Deprecated
26
 
class RussianStemmer
27
 
{
28
 
    // positions of RV, R1 and R2 respectively
29
 
    private int RV, /*R1,*/ R2;
30
 
 
31
 
    // letters (currently unused letters are commented out)
32
 
    private final static char A = '\u0430';
33
 
    //private final static char B = '\u0431';
34
 
    private final static char V = '\u0432';
35
 
    private final static char G = '\u0433';
36
 
    //private final static char D = '\u0434';
37
 
    private final static char E = '\u0435';
38
 
    //private final static char ZH = '\u0436';
39
 
    //private final static char Z = '\u0437';
40
 
    private final static char I = '\u0438';
41
 
    private final static char I_ = '\u0439';
42
 
    //private final static char K = '\u043A';
43
 
    private final static char L = '\u043B';
44
 
    private final static char M = '\u043C';
45
 
    private final static char N = '\u043D';
46
 
    private final static char O = '\u043E';
47
 
    //private final static char P = '\u043F';
48
 
    //private final static char R = '\u0440';
49
 
    private final static char S = '\u0441';
50
 
    private final static char T = '\u0442';
51
 
    private final static char U = '\u0443';
52
 
    //private final static char F = '\u0444';
53
 
    private final static char X = '\u0445';
54
 
    //private final static char TS = '\u0446';
55
 
    //private final static char CH = '\u0447';
56
 
    private final static char SH = '\u0448';
57
 
    private final static char SHCH = '\u0449';
58
 
    //private final static char HARD = '\u044A';
59
 
    private final static char Y = '\u044B';
60
 
    private final static char SOFT = '\u044C';
61
 
    private final static char AE = '\u044D';
62
 
    private final static char IU = '\u044E';
63
 
    private final static char IA = '\u044F';
64
 
 
65
 
    // stem definitions
66
 
    private static char[] vowels = { A, E, I, O, U, Y, AE, IU, IA };
67
 
 
68
 
    private static char[][] perfectiveGerundEndings1 = {
69
 
        { V },
70
 
        { V, SH, I },
71
 
        { V, SH, I, S, SOFT }
72
 
    };
73
 
 
74
 
    private static char[][] perfectiveGerund1Predessors = {
75
 
        { A },
76
 
        { IA }
77
 
    };
78
 
 
79
 
    private static char[][] perfectiveGerundEndings2 = { { I, V }, {
80
 
        Y, V }, {
81
 
            I, V, SH, I }, {
82
 
                Y, V, SH, I }, {
83
 
                    I, V, SH, I, S, SOFT }, {
84
 
                        Y, V, SH, I, S, SOFT }
85
 
    };
86
 
 
87
 
    private static char[][] adjectiveEndings = {
88
 
        { E, E },
89
 
        { I, E },
90
 
        { Y, E },
91
 
        { O, E },
92
 
        { E, I_ },
93
 
        { I, I_ },
94
 
        { Y, I_ },
95
 
        { O, I_ },
96
 
        { E, M },
97
 
        { I, M },
98
 
        { Y, M },
99
 
        { O, M },
100
 
        { I, X },
101
 
        { Y, X },
102
 
        { U, IU },
103
 
        { IU, IU },
104
 
        { A, IA },
105
 
        { IA, IA },
106
 
        { O, IU },
107
 
        { E, IU },
108
 
        { I, M, I },
109
 
        { Y, M, I },
110
 
        { E, G, O },
111
 
        { O, G, O },
112
 
        { E, M, U },
113
 
        {O, M, U }
114
 
    };
115
 
 
116
 
    private static char[][] participleEndings1 = {
117
 
        { SHCH },
118
 
        { E, M },
119
 
        { N, N },
120
 
        { V, SH },
121
 
        { IU, SHCH }
122
 
    };
123
 
 
124
 
    private static char[][] participleEndings2 = {
125
 
        { I, V, SH },
126
 
        { Y, V, SH },
127
 
        { U, IU, SHCH }
128
 
    };
129
 
 
130
 
    private static char[][] participle1Predessors = {
131
 
        { A },
132
 
        { IA }
133
 
    };
134
 
 
135
 
    private static char[][] reflexiveEndings = {
136
 
        { S, IA },
137
 
        { S, SOFT }
138
 
    };
139
 
 
140
 
    private static char[][] verbEndings1 = {
141
 
        { I_ },
142
 
        { L },
143
 
        { N },
144
 
        { L, O },
145
 
        { N, O },
146
 
        { E, T },
147
 
        { IU, T },
148
 
        { L, A },
149
 
        { N, A },
150
 
        { L, I },
151
 
        { E, M },
152
 
        { N, Y },
153
 
        { E, T, E },
154
 
        { I_, T, E },
155
 
        { T, SOFT },
156
 
        { E, SH, SOFT },
157
 
        { N, N, O }
158
 
    };
159
 
 
160
 
    private static char[][] verbEndings2 = {
161
 
        { IU },
162
 
        { U, IU },
163
 
        { E, N },
164
 
        { E, I_ },
165
 
        { IA, T },
166
 
        { U, I_ },
167
 
        { I, L },
168
 
        { Y, L },
169
 
        { I, M },
170
 
        { Y, M },
171
 
        { I, T },
172
 
        { Y, T },
173
 
        { I, L, A },
174
 
        { Y, L, A },
175
 
        { E, N, A },
176
 
        { I, T, E },
177
 
        { I, L, I },
178
 
        { Y, L, I },
179
 
        { I, L, O },
180
 
        { Y, L, O },
181
 
        { E, N, O },
182
 
        { U, E, T },
183
 
        { U, IU, T },
184
 
        { E, N, Y },
185
 
        { I, T, SOFT },
186
 
        { Y, T, SOFT },
187
 
        { I, SH, SOFT },
188
 
        { E, I_, T, E },
189
 
        { U, I_, T, E }
190
 
    };
191
 
 
192
 
    private static char[][] verb1Predessors = {
193
 
        { A },
194
 
        { IA }
195
 
    };
196
 
 
197
 
    private static char[][] nounEndings = {
198
 
        { A },
199
 
        { U },
200
 
        { I_ },
201
 
        { O },
202
 
        { U },
203
 
        { E },
204
 
        { Y },
205
 
        { I },
206
 
        { SOFT },
207
 
        { IA },
208
 
        { E, V },
209
 
        { O, V },
210
 
        { I, E },
211
 
        { SOFT, E },
212
 
        { IA, X },
213
 
        { I, IU },
214
 
        { E, I },
215
 
        { I, I },
216
 
        { E, I_ },
217
 
        { O, I_ },
218
 
        { E, M },
219
 
        { A, M },
220
 
        { O, M },
221
 
        { A, X },
222
 
        { SOFT, IU },
223
 
        { I, IA },
224
 
        { SOFT, IA },
225
 
        { I, I_ },
226
 
        { IA, M },
227
 
        { IA, M, I },
228
 
        { A, M, I },
229
 
        { I, E, I_ },
230
 
        { I, IA, M },
231
 
        { I, E, M },
232
 
        { I, IA, X },
233
 
        { I, IA, M, I }
234
 
    };
235
 
 
236
 
    private static char[][] superlativeEndings = {
237
 
        { E, I_, SH },
238
 
        { E, I_, SH, E }
239
 
    };
240
 
 
241
 
    private static char[][] derivationalEndings = {
242
 
        { O, S, T },
243
 
        { O, S, T, SOFT }
244
 
    };
245
 
 
246
 
    /**
247
 
     * RussianStemmer constructor comment.
248
 
     */
249
 
    public RussianStemmer()
250
 
    {
251
 
        super();
252
 
    }
253
 
 
254
 
    /**
255
 
     * Adjectival ending is an adjective ending,
256
 
     * optionally preceded by participle ending.
257
 
     * Creation date: (17/03/2002 12:14:58 AM)
258
 
     * @param stemmingZone java.lang.StringBuilder
259
 
     */
260
 
    private boolean adjectival(StringBuilder stemmingZone)
261
 
    {
262
 
        // look for adjective ending in a stemming zone
263
 
        if (!findAndRemoveEnding(stemmingZone, adjectiveEndings))
264
 
            return false;
265
 
        // if adjective ending was found, try for participle ending.
266
 
        if (!findAndRemoveEnding(stemmingZone, participleEndings1, participle1Predessors))
267
 
            findAndRemoveEnding(stemmingZone, participleEndings2);
268
 
        return true;
269
 
    }
270
 
 
271
 
    /**
272
 
     * Derivational endings
273
 
     * Creation date: (17/03/2002 12:14:58 AM)
274
 
     * @param stemmingZone java.lang.StringBuilder
275
 
     */
276
 
    private boolean derivational(StringBuilder stemmingZone)
277
 
    {
278
 
        int endingLength = findEnding(stemmingZone, derivationalEndings);
279
 
        if (endingLength == 0)
280
 
             // no derivational ending found
281
 
            return false;
282
 
        else
283
 
        {
284
 
            // Ensure that the ending locates in R2
285
 
            if (R2 - RV <= stemmingZone.length() - endingLength)
286
 
            {
287
 
                stemmingZone.setLength(stemmingZone.length() - endingLength);
288
 
                return true;
289
 
            }
290
 
            else
291
 
            {
292
 
                return false;
293
 
            }
294
 
        }
295
 
    }
296
 
 
297
 
    /**
298
 
     * Finds ending among given ending class and returns the length of ending found(0, if not found).
299
 
     * Creation date: (17/03/2002 8:18:34 PM)
300
 
     */
301
 
    private int findEnding(StringBuilder stemmingZone, int startIndex, char[][] theEndingClass)
302
 
    {
303
 
        boolean match = false;
304
 
        for (int i = theEndingClass.length - 1; i >= 0; i--)
305
 
        {
306
 
            char[] theEnding = theEndingClass[i];
307
 
            // check if the ending is bigger than stemming zone
308
 
            if (startIndex < theEnding.length - 1)
309
 
            {
310
 
                match = false;
311
 
                continue;
312
 
            }
313
 
            match = true;
314
 
            int stemmingIndex = startIndex;
315
 
            for (int j = theEnding.length - 1; j >= 0; j--)
316
 
            {
317
 
                if (stemmingZone.charAt(stemmingIndex--) != theEnding[j])
318
 
                {
319
 
                    match = false;
320
 
                    break;
321
 
                }
322
 
            }
323
 
            // check if ending was found
324
 
            if (match)
325
 
            {
326
 
                return theEndingClass[i].length; // cut ending
327
 
            }
328
 
        }
329
 
        return 0;
330
 
    }
331
 
 
332
 
    private int findEnding(StringBuilder stemmingZone, char[][] theEndingClass)
333
 
    {
334
 
        return findEnding(stemmingZone, stemmingZone.length() - 1, theEndingClass);
335
 
    }
336
 
 
337
 
    /**
338
 
     * Finds the ending among the given class of endings and removes it from stemming zone.
339
 
     * Creation date: (17/03/2002 8:18:34 PM)
340
 
     */
341
 
    private boolean findAndRemoveEnding(StringBuilder stemmingZone, char[][] theEndingClass)
342
 
    {
343
 
        int endingLength = findEnding(stemmingZone, theEndingClass);
344
 
        if (endingLength == 0)
345
 
            // not found
346
 
            return false;
347
 
        else {
348
 
            stemmingZone.setLength(stemmingZone.length() - endingLength);
349
 
            // cut the ending found
350
 
            return true;
351
 
        }
352
 
    }
353
 
 
354
 
    /**
355
 
     * Finds the ending among the given class of endings, then checks if this ending was
356
 
     * preceded by any of given predecessors, and if so, removes it from stemming zone.
357
 
     * Creation date: (17/03/2002 8:18:34 PM)
358
 
     */
359
 
    private boolean findAndRemoveEnding(StringBuilder stemmingZone,
360
 
        char[][] theEndingClass, char[][] thePredessors)
361
 
    {
362
 
        int endingLength = findEnding(stemmingZone, theEndingClass);
363
 
        if (endingLength == 0)
364
 
            // not found
365
 
            return false;
366
 
        else
367
 
        {
368
 
            int predessorLength =
369
 
                findEnding(stemmingZone,
370
 
                    stemmingZone.length() - endingLength - 1,
371
 
                    thePredessors);
372
 
            if (predessorLength == 0)
373
 
                return false;
374
 
            else {
375
 
                stemmingZone.setLength(stemmingZone.length() - endingLength);
376
 
                // cut the ending found
377
 
                return true;
378
 
            }
379
 
        }
380
 
 
381
 
    }
382
 
 
383
 
    /**
384
 
     * Marks positions of RV, R1 and R2 in a given word.
385
 
     * Creation date: (16/03/2002 3:40:11 PM)
386
 
     */
387
 
    private void markPositions(String word)
388
 
    {
389
 
        RV = 0;
390
 
//        R1 = 0;
391
 
        R2 = 0;
392
 
        int i = 0;
393
 
        // find RV
394
 
        while (word.length() > i && !isVowel(word.charAt(i)))
395
 
        {
396
 
            i++;
397
 
        }
398
 
        if (word.length() - 1 < ++i)
399
 
            return; // RV zone is empty
400
 
        RV = i;
401
 
        // find R1
402
 
        while (word.length() > i && isVowel(word.charAt(i)))
403
 
        {
404
 
            i++;
405
 
        }
406
 
        if (word.length() - 1 < ++i)
407
 
            return; // R1 zone is empty
408
 
//        R1 = i;
409
 
        // find R2
410
 
        while (word.length() > i && !isVowel(word.charAt(i)))
411
 
        {
412
 
            i++;
413
 
        }
414
 
        if (word.length() - 1 < ++i)
415
 
            return; // R2 zone is empty
416
 
        while (word.length() > i && isVowel(word.charAt(i)))
417
 
        {
418
 
            i++;
419
 
        }
420
 
        if (word.length() - 1 < ++i)
421
 
            return; // R2 zone is empty
422
 
        R2 = i;
423
 
    }
424
 
 
425
 
    /**
426
 
     * Checks if character is a vowel..
427
 
     * Creation date: (16/03/2002 10:47:03 PM)
428
 
     * @return boolean
429
 
     * @param letter char
430
 
     */
431
 
    private boolean isVowel(char letter)
432
 
    {
433
 
        for (int i = 0; i < vowels.length; i++)
434
 
        {
435
 
            if (letter == vowels[i])
436
 
                return true;
437
 
        }
438
 
        return false;
439
 
    }
440
 
 
441
 
    /**
442
 
     * Noun endings.
443
 
     * Creation date: (17/03/2002 12:14:58 AM)
444
 
     * @param stemmingZone java.lang.StringBuilder
445
 
     */
446
 
    private boolean noun(StringBuilder stemmingZone)
447
 
    {
448
 
        return findAndRemoveEnding(stemmingZone, nounEndings);
449
 
    }
450
 
 
451
 
    /**
452
 
     * Perfective gerund endings.
453
 
     * Creation date: (17/03/2002 12:14:58 AM)
454
 
     * @param stemmingZone java.lang.StringBuilder
455
 
     */
456
 
    private boolean perfectiveGerund(StringBuilder stemmingZone)
457
 
    {
458
 
        return findAndRemoveEnding(
459
 
            stemmingZone,
460
 
            perfectiveGerundEndings1,
461
 
            perfectiveGerund1Predessors)
462
 
            || findAndRemoveEnding(stemmingZone, perfectiveGerundEndings2);
463
 
    }
464
 
 
465
 
    /**
466
 
     * Reflexive endings.
467
 
     * Creation date: (17/03/2002 12:14:58 AM)
468
 
     * @param stemmingZone java.lang.StringBuilder
469
 
     */
470
 
    private boolean reflexive(StringBuilder stemmingZone)
471
 
    {
472
 
        return findAndRemoveEnding(stemmingZone, reflexiveEndings);
473
 
    }
474
 
 
475
 
    /**
476
 
     * Insert the method's description here.
477
 
     * Creation date: (17/03/2002 12:14:58 AM)
478
 
     * @param stemmingZone java.lang.StringBuilder
479
 
     */
480
 
    private boolean removeI(StringBuilder stemmingZone)
481
 
    {
482
 
        if (stemmingZone.length() > 0
483
 
            && stemmingZone.charAt(stemmingZone.length() - 1) == I)
484
 
        {
485
 
            stemmingZone.setLength(stemmingZone.length() - 1);
486
 
            return true;
487
 
        }
488
 
        else
489
 
        {
490
 
            return false;
491
 
        }
492
 
    }
493
 
 
494
 
    /**
495
 
     * Insert the method's description here.
496
 
     * Creation date: (17/03/2002 12:14:58 AM)
497
 
     * @param stemmingZone java.lang.StringBuilder
498
 
     */
499
 
    private boolean removeSoft(StringBuilder stemmingZone)
500
 
    {
501
 
        if (stemmingZone.length() > 0
502
 
            && stemmingZone.charAt(stemmingZone.length() - 1) == SOFT)
503
 
        {
504
 
            stemmingZone.setLength(stemmingZone.length() - 1);
505
 
            return true;
506
 
        }
507
 
        else
508
 
        {
509
 
            return false;
510
 
        }
511
 
    }
512
 
 
513
 
    /**
514
 
     * Finds the stem for given Russian word.
515
 
     * Creation date: (16/03/2002 3:36:48 PM)
516
 
     * @return java.lang.String
517
 
     * @param input java.lang.String
518
 
     */
519
 
    public String stem(String input)
520
 
    {
521
 
        markPositions(input);
522
 
        if (RV == 0)
523
 
            return input; //RV wasn't detected, nothing to stem
524
 
        StringBuilder stemmingZone = new StringBuilder(input.substring(RV));
525
 
        // stemming goes on in RV
526
 
        // Step 1
527
 
 
528
 
        if (!perfectiveGerund(stemmingZone))
529
 
        {
530
 
            reflexive(stemmingZone);
531
 
            if (!adjectival(stemmingZone))
532
 
              if (!verb(stemmingZone))
533
 
                noun(stemmingZone);
534
 
        }
535
 
        // Step 2
536
 
        removeI(stemmingZone);
537
 
        // Step 3
538
 
        derivational(stemmingZone);
539
 
        // Step 4
540
 
        superlative(stemmingZone);
541
 
        undoubleN(stemmingZone);
542
 
        removeSoft(stemmingZone);
543
 
        // return result
544
 
        return input.substring(0, RV) + stemmingZone.toString();
545
 
    }
546
 
 
547
 
    /**
548
 
     * Superlative endings.
549
 
     * Creation date: (17/03/2002 12:14:58 AM)
550
 
     * @param stemmingZone java.lang.StringBuilder
551
 
     */
552
 
    private boolean superlative(StringBuilder stemmingZone)
553
 
    {
554
 
        return findAndRemoveEnding(stemmingZone, superlativeEndings);
555
 
    }
556
 
 
557
 
    /**
558
 
     * Undoubles N.
559
 
     * Creation date: (17/03/2002 12:14:58 AM)
560
 
     * @param stemmingZone java.lang.StringBuilder
561
 
     */
562
 
    private boolean undoubleN(StringBuilder stemmingZone)
563
 
    {
564
 
        char[][] doubleN = {
565
 
            { N, N }
566
 
        };
567
 
        if (findEnding(stemmingZone, doubleN) != 0)
568
 
        {
569
 
            stemmingZone.setLength(stemmingZone.length() - 1);
570
 
            return true;
571
 
        }
572
 
        else
573
 
        {
574
 
            return false;
575
 
        }
576
 
    }
577
 
 
578
 
    /**
579
 
     * Verb endings.
580
 
     * Creation date: (17/03/2002 12:14:58 AM)
581
 
     * @param stemmingZone java.lang.StringBuilder
582
 
     */
583
 
    private boolean verb(StringBuilder stemmingZone)
584
 
    {
585
 
        return findAndRemoveEnding(
586
 
            stemmingZone,
587
 
            verbEndings1,
588
 
            verb1Predessors)
589
 
            || findAndRemoveEnding(stemmingZone, verbEndings2);
590
 
    }
591
 
   
592
 
    /**
593
 
     * Static method for stemming.
594
 
     */
595
 
    public static String stemWord(String theWord)
596
 
    {
597
 
        RussianStemmer stemmer = new RussianStemmer();
598
 
        return stemmer.stem(theWord);
599
 
    }
600
 
}