/// <summary> /// Searches the automaton for a symbol sequence equal to <paramref name="word"/>, /// followed by a separator. The result is a stem (decompressed accordingly /// to the dictionary's specification) and an optional tag data. /// </summary> public IList <WordData> Lookup(ICharSequence word) { byte separator = dictionaryMetadata.Separator; #pragma warning disable 612, 618 int prefixBytes = sequenceEncoder.PrefixBytes; #pragma warning restore 612, 618 if (dictionaryMetadata.InputConversionPairs.Any()) { word = ApplyReplacements(word, dictionaryMetadata.InputConversionPairs); } // Reset the output list to zero length. formsList.Wrap(forms, 0, 0); // Encode word characters into bytes in the same encoding as the FSA's. charBuffer = BufferUtils.ClearAndEnsureCapacity(charBuffer, word.Length); for (int i = 0; i < word.Length; i++) { char chr = word[i]; if (chr == separatorChar) { // No valid input can contain the separator. return(formsList); } charBuffer.Put(chr); } charBuffer.Flip(); try { byteBuffer = BufferUtils.CharsToBytes(encoder, charBuffer, byteBuffer); } catch (UnmappableInputException) { // This should be a rare occurrence, but if it happens it means there is no way // the dictionary can contain the input word. return(formsList); } // Try to find a partial match in the dictionary. MatchResult match = matcher.Match(matchResult, byteBuffer .Array, 0, byteBuffer.Remaining, rootNode); if (match.Kind == MatchResult.SequenceIsAPrefix) { /* * The entire sequence exists in the dictionary. A separator should * be the next symbol. */ int arc = fsa.GetArc(match.Node, separator); /* * The situation when the arc points to a final node should NEVER * happen. After all, we want the word to have SOME base form. */ if (arc != 0 && !fsa.IsArcFinal(arc)) { // There is such a word in the dictionary. Return its base forms. int formsCount = 0; finalStatesIterator.RestartFrom(fsa.GetEndNode(arc)); while (finalStatesIterator.MoveNext()) { ByteBuffer bb = finalStatesIterator.Current; byte[] ba = bb.Array; int bbSize = bb.Remaining; if (formsCount >= forms.Length) { //forms = Arrays.CopyOf(forms, forms.Length + EXPAND_SIZE); Array.Resize(ref forms, forms.Length + ExpandSize); for (int k = 0; k < forms.Length; k++) { if (forms[k] == null) { forms[k] = new WordData(decoder); } } } /* * Now, expand the prefix/ suffix 'compression' and store * the base form. */ WordData wordData = forms[formsCount++]; if (!dictionaryMetadata.OutputConversionPairs.Any()) { wordData.Update(byteBuffer, word); } else { wordData.Update(byteBuffer, ApplyReplacements(word, dictionaryMetadata.OutputConversionPairs)); } /* * Find the separator byte's position splitting the inflection instructions * from the tag. */ Debug.Assert(prefixBytes <= bbSize, sequenceEncoder.GetType() + " >? " + bbSize); int sepPos; for (sepPos = prefixBytes; sepPos < bbSize; sepPos++) { if (ba[sepPos] == separator) { break; } } /* * Decode the stem into stem buffer. */ wordData.stemBuffer = sequenceEncoder.Decode(wordData.stemBuffer, byteBuffer, ByteBuffer.Wrap(ba, 0, sepPos)); // Skip separator character. sepPos++; /* * Decode the tag data. */ int tagSize = bbSize - sepPos; if (tagSize > 0) { wordData.tagBuffer = BufferUtils.ClearAndEnsureCapacity(wordData.tagBuffer, tagSize); wordData.tagBuffer.Put(ba, sepPos, tagSize); wordData.tagBuffer.Flip(); } } formsList.Wrap(forms, 0, formsCount); } } else { /* * this case is somewhat confusing: we should have hit the separator * first... I don't really know how to deal with it at the time * being. */ } return(formsList); }