add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / contrib / analyzers / common / src / java / org / apache / lucene / analysis / ru / RussianStemmer.java
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 }