blob: 4590b5b385493d3c5815a1c8dfb410077cb7fa37 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package com.adobe.linguistics.spelling.core.rule
{
import com.adobe.linguistics.spelling.core.HashEntry;
import com.adobe.linguistics.spelling.core.env.InternalConstants;
import com.adobe.linguistics.spelling.core.utils.StringUtils;
public class OptimizedSuffixEntry extends AffixEntry
{
private var _flagNext:OptimizedSuffixEntry;
private var _keyNext:OptimizedSuffixEntry;
private var _flags:Array;
private var _sfxTable:Array;
private var _reverseAffixKey:String;
public function OptimizedSuffixEntry(sfxEntry:SuffixEntry)
{
super(sfxEntry.flag,sfxEntry.stripValue,sfxEntry.affixKey,sfxEntry.conditionString,sfxEntry.morphologicalFields, sfxEntry.permissionToCombine, sfxEntry.type,sfxEntry.contclass);
_flags = new Array();
_sfxTable = new Array();
this.nextElementWithFlag = null;
this.nextElementWithKey = null;
_flags.push(this.flag);
_sfxTable.push(sfxEntry);
this.reverseAffixKey = StringUtils.reverseString(this.affixKey);
this.flag = -1;
this.conditionString = "";
}
public function isSimilarObject(sfxEntry:SuffixEntry):Boolean {
var chkString:String=this.contclass+sfxEntry.contclass;
if(chkString)chkString=chkString.split('').sort().join('').replace(/(.)\1+/gi,'$1');//this pattern removes any repetition from strings. this will work only because we are converting n' or q' to Long numbers in decode flags
if ( (this.stripValue == sfxEntry.stripValue) && (this.affixKey == sfxEntry.affixKey) && (this.permissionToCombine == sfxEntry.permissionToCombine) && (this.morphologicalFields == sfxEntry.morphologicalFields) &&(this.contclass==chkString) ) return true;
return false;
}
public function extendObject( sfxEntry:SuffixEntry ):Boolean {
if ( !isSimilarObject(sfxEntry) ) return false;
_flags.push(sfxEntry.flag);
_sfxTable.push(sfxEntry);
var newConditionString:String;
newConditionString = this.conditionPattern.source + "|" + "^"+".*"+sfxEntry.conditionString+"$";
this.conditionPattern = new RegExp ( newConditionString);
//now add in contclass
this.contclass=sfxEntry.contclass;
return true;
}
public function set reverseAffixKey(value:String):void {
this._reverseAffixKey = value;
}
public function get reverseAffixKey():String {
return this._reverseAffixKey;
}
public function get nextElementWithKey():OptimizedSuffixEntry {
return this._keyNext;
}
public function set nextElementWithKey(pfxEntry:OptimizedSuffixEntry):void {
this._keyNext = pfxEntry;
}
public function get nextElementWithFlag():OptimizedSuffixEntry {
return this._flagNext;
}
public function set nextElementWithFlag(pfxEntry:OptimizedSuffixEntry):void {
this._flagNext = pfxEntry;
}
public function get flags():Array {
return this._flags;
}
/*
* Deprecated function for now...
* History:
* A pre-version of implementation for error detection. After I optimized the code for performance,
* I drop this function by that time, but you know performance meassuring is a tricky problem...
* ToDo: Need a revisit when we implementing complex-affix support and compound-word support.
*/
// see if this suffix is present in the word
public function checkWord( word:String, needFlag:int, inCompound:int):HashEntry {
var disLen:int = word.length - this.affixKey.length;
var he:HashEntry = null;
var i:int;
// upon entry suffix is 0 length or already matches the end of the word.
// So if the remaining root word has positive length
// and if there are enough chars in root word and added back strip chars
// to meet the number of characters conditions, then test it
if ( (disLen > 0 || (disLen == 0 && this.attributeManager.fullStrip)) ) {
// generate new root word by removing suffix and adding
// back any characters that would have been stripped or
// or null terminating the shorter string
word = word.substr(0, word.length - this.affixKey.length) + this.stripValue;
// now make sure all of the conditions on characters
// are met. Please see the appendix at the end of
// this file for more info on exactly what is being
// tested
// if all conditions are met then check if resulting
// root word in the dictionary
if ( this.conditionPattern.test( word ) ) {
// look word in hash table
for ( i=0; i < this.attributeManager.dictionaryManager.dictonaryList.length && !he; ++i ) {
he = this.attributeManager.dictionaryManager.dictonaryList[i].getElement(word);
while( he ) {
if ( he.testAffixs(this._flags) && ( (!needFlag) || he.testAffix(needFlag) ) ) {
return he;
}
he = he.next;
}
}
//if ((opts & aeXPRODUCT) && in_compound)
if ( this.permissionToCombine ) {
he = this.attributeManager.optPrefixCheck(word, InternalConstants.aeXPRODUCT,this, needFlag, inCompound);
if (he) return he;
}
}
}
return he;
}
//for develepors only, function used for printing flags when flag_mode=FLAG.LONG presently not being called anywhere
public function printFlag(flag:Number):void{
var result:String = String.fromCharCode(flag>>8) + String.fromCharCode(flag-((flag>>8)<<8));
var x:String= this.affixKey;
}
// see if this suffix is present in the word
public function checkWord2( word:String, sfxopts:int, ppfx:AffixEntry, needFlag:int, inCompound:int, cclass:int, pfxcclass:int=0):HashEntry {
var disLen:int = word.length - this.affixKey.length;
var he:HashEntry = null;
var i:int;
// if this suffix is being cross checked with a prefix
// but it does not support cross products skip it
if ( (sfxopts& InternalConstants.aeXPRODUCT) != 0 && this.permissionToCombine != true ) return null;
// upon entry suffix is 0 length or already matches the end of the word.
// So if the remaining root word has positive length
// and if there are enough chars in root word and added back strip chars
// to meet the number of characters conditions, then test it
if ( (disLen > 0 || (disLen == 0 && this.attributeManager.fullStrip)) ) {
// generate new root word by removing suffix and adding
// back any characters that would have been stripped or
// or null terminating the shorter string
word = word.substr(0, word.length - this.affixKey.length) + this.stripValue;
// now make sure all of the conditions on characters
// are met. Please see the appendix at the end of
// this file for more info on exactly what is being
// tested
// if all conditions are met then check if resulting
// root word in the dictionary
if ( this.conditionPattern.test( word ) ) {
// look word in hash table
for ( i=0; i < this.attributeManager.dictionaryManager.dictonaryList.length && !he; ++i ) {
he = this.attributeManager.dictionaryManager.dictonaryList[i].getElement(word);
while( he ) {
if ( (( he.testAffixs(this._flags) ) && ( (!needFlag) || he.testAffix(needFlag) ))||(ppfx && ppfx.contclass) ) {
for ( var j:int=0;j<this._sfxTable.length;++j) {
if ( (this._sfxTable[j] ).conditionPattern.test(word) ) {
if(!ppfx)
{
if(cclass)
{
if (he.testAffix(this._flags[j]) && HashEntry.TESTAFF(this.contclass,cclass) )//should handle cases like drink->able->s also in un-run-able-s if run-->able and able-->s and s-->un this should suffice
return he;
}
else
{ if(he.testAffix(this._flags[j]))//should handle all normal cases like drink->able or drink->s
return he;
}
}
else
{
if(this.contclass && he.testAffix(this._flags[j]) && HashEntry.TESTAFF(this.contclass,cclass) && !pfxcclass) // handle when suffix has contclass like l'->autre->s
{
return he;
}
if(ppfx.contclass && HashEntry.TESTAFF(ppfx.contclass,this._flags[j]) && he.testAffix(cclass) && !pfxcclass) //handle when prefix has contclass like milli->litre->s
{
return he;
}
if(he.testAffix(this._flags[j]) && he.testAffix(cclass))//handle normal cases when both pfx and sfx exist in hash affix string
{
return he;
}
//special case of un-drink-able-s
if( (he.testAffix(pfxcclass) && ppfx.contclass && HashEntry.TESTAFF(ppfx.contclass,this._flags[j]) && this.contclass && HashEntry.TESTAFF(this.contclass,cclass))
|| (he.testAffix(this._flags[j]) && this.contclass && HashEntry.TESTAFF(this.contclass,cclass) && HashEntry.TESTAFF(this.contclass,pfxcclass))
)
{
return he;
}
}
}
}
}
he = he.next;
}
}
}
}
return he;
}
// Function for two level suffix checkword
// see if this suffix is present in the word
public function checkTwoWord( word:String, sfxopts:int, ppfx:AffixEntry, needFlag:int, cclass:int, pfxcclass:int=0):HashEntry {
var disLen:int = word.length - this.affixKey.length;
var he:HashEntry = null;
var i:int;
// if this suffix is being cross checked with a prefix
// but it does not support cross products skip it
if ( (sfxopts& InternalConstants.aeXPRODUCT) != 0 && this.permissionToCombine != true ) return null;
// upon entry suffix is 0 length or already matches the end of the word.
// So if the remaining root word has positive length
// and if there are enough chars in root word and added back strip chars
// to meet the number of characters conditions, then test it
if ( (disLen > 0 || (disLen == 0 && this.attributeManager.fullStrip)) ) {
// generate new root word by removing suffix and adding
// back any characters that would have been stripped or
// or null terminating the shorter string
word = word.substr(0, word.length - this.affixKey.length) + this.stripValue;
// now make sure all of the conditions on characters
// are met. Please see the appendix at the end of
// this file for more info on exactly what is being
// tested
// if all conditions are met then see if for conditional suffix and if this has been stripped by a possible
// contclass check the remaining word
// eg: if drinkables was original word and after possible stripping of s we have drinkable very if
// now check drinkable, able will be stripped and drink will be found that hash entry will then be returned
if ( this.conditionPattern.test( word ) ) {//checks a whole group of
if(ppfx)
{ //check for conditional suffix
if( contclass!=null && HashEntry.TESTAFF(contclass, pfxcclass))
{
he = this.attributeManager.optSuffixCheck2(word, 0, null,needFlag,0,cclass,pfxcclass);//we are not sending ppfx here as it will not be needed.
}
else
{
he = this.attributeManager.optSuffixCheck2(word, sfxopts, ppfx,needFlag,0,cclass,pfxcclass);
}
}
else
{
he = this.attributeManager.optSuffixCheck2(word, 0, null,needFlag,0,cclass,0);
}
if (he) {
for ( var j:int=0;j<this._sfxTable.length;++j) { //Squiggly will handle drink->able->s from here
if ( (this._sfxTable[j]).conditionPattern.test(word) && cclass==(this._sfxTable[j]).flag) {//only permit words which end with s in drinkables
return he;
}
}
he = null;
}
}
}
return he;
}
//--
}
}