├── .gitattributes ├── .gitignore ├── AutoCorrect.h ├── AutoCorrect.m ├── AvroKeyboard.xcodeproj ├── jh.mode1v3 ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── AvroKeyboardController.h ├── AvroKeyboardController.m ├── AvroKeyboard_Prefix.pch ├── AvroParser.h ├── AvroParser.m ├── CacheManager.h ├── CacheManager.m ├── Candidates.h ├── Candidates.m ├── Credits.rtfd ├── Pasted Graphic 1.tiff ├── Pasted Graphic 2.tiff ├── Pasted Graphic 4.tiff ├── Pasted Graphic 5.tiff └── TXT.rtf ├── Database.h ├── Database.m ├── English.lproj ├── InfoPlist.strings ├── MainMenu.nib │ ├── designable.nib │ └── keyedobjects.nib └── preferences.nib │ ├── designable.nib │ └── keyedobjects.nib ├── FMDatabase.h ├── FMDatabase.m ├── FMDatabasePool.h ├── FMDatabasePool.m ├── FMResultSet.h ├── FMResultSet.m ├── Icons ├── AutoCorrect.png ├── Credits.png └── General.png ├── Info.plist ├── MainMenuAppDelegate.h ├── MainMenuAppDelegate.m ├── NSString+Levenshtein.h ├── NSString+Levenshtein.m ├── PreferencesController.h ├── PreferencesController.m ├── README.md ├── RegexKitLite.h ├── RegexKitLite.m ├── RegexParser.h ├── RegexParser.m ├── Suggestion.h ├── Suggestion.m ├── autodict.dct ├── avro.icns ├── data.json ├── database.db3 ├── main.m ├── preferences.plist └── regex.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.db3 binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.gitignore 3 | !.gitattributes 4 | 5 | # User specific settings folder 6 | AvroKeyboard.xcodeproj/xcuserdata/ 7 | AvroKeyboard.xcodeproj/project.xcworkspace/xcuserdata/ 8 | -------------------------------------------------------------------------------- /AutoCorrect.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/24/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface AutoCorrect : NSObject { 11 | NSMutableArray* _autoCorrectEntries; 12 | } 13 | 14 | @property (retain) NSMutableArray* autoCorrectEntries; 15 | 16 | + (AutoCorrect *)sharedInstance; 17 | 18 | - (NSString*)find:(NSString*)term; 19 | - (NSMutableArray*)autoCorrectEntries; 20 | - (void)setAutoCorrectEntries:(NSMutableArray *)autoCorrectEntries; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /AutoCorrect.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/24/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "AutoCorrect.h" 9 | #import "AvroParser.h" 10 | 11 | static AutoCorrect* sharedInstance = nil; 12 | 13 | @implementation AutoCorrect 14 | 15 | @synthesize autoCorrectEntries = _autoCorrectEntries; 16 | 17 | + (AutoCorrect *)sharedInstance { 18 | if (sharedInstance == nil) { 19 | [[self alloc] init]; // assignment not done here, see allocWithZone 20 | } 21 | return sharedInstance; 22 | } 23 | 24 | + (id)allocWithZone:(NSZone *)zone { 25 | if (sharedInstance == nil) { 26 | sharedInstance = [super allocWithZone:zone]; 27 | return sharedInstance; // assignment and return on first allocation 28 | } 29 | return sharedInstance; //on subsequent allocation attempts return nil 30 | } 31 | 32 | - (id)copyWithZone:(NSZone *)zone { 33 | return self; 34 | } 35 | 36 | - (id)retain { 37 | return self; 38 | } 39 | 40 | - (oneway void)release { 41 | //do nothing 42 | } 43 | 44 | - (id)autorelease { 45 | return self; 46 | } 47 | 48 | - (NSUInteger)retainCount { 49 | return NSUIntegerMax; // This is sooo not zero 50 | } 51 | 52 | - (id)init { 53 | self = [super init]; 54 | if (self) { 55 | // Open the file 56 | NSString *fileName = [[NSBundle mainBundle] pathForResource:@"autodict" ofType:@"dct"]; 57 | const char *fn = [fileName UTF8String]; 58 | FILE *file = fopen(fn, "r"); 59 | 60 | // Read from the file 61 | char replaceBuffer[512], withBuffer[512]; 62 | _autoCorrectEntries = [[NSMutableArray alloc] init]; 63 | while(fscanf(file, "%s %[^\n]\n", replaceBuffer, withBuffer) == 2) { 64 | NSString* replace = [NSString stringWithFormat:@"%s", replaceBuffer]; 65 | NSString* with = [NSString stringWithFormat:@"%s", withBuffer]; 66 | 67 | if ([replace isEqualToString:with] == NO) { 68 | with = [[AvroParser sharedInstance] parse:with]; 69 | } 70 | 71 | NSMutableDictionary* item = [[NSMutableDictionary alloc] initWithObjectsAndKeys:replace, @"replace", with, @"with", nil]; 72 | [_autoCorrectEntries addObject:item]; 73 | [item release]; 74 | } 75 | fclose(file); 76 | } 77 | return self; 78 | } 79 | 80 | - (void)dealloc { 81 | [_autoCorrectEntries release]; 82 | [super dealloc]; 83 | } 84 | 85 | // Instance Methods 86 | - (NSString*)find:(NSString*)term { 87 | term = [[AvroParser sharedInstance] fix:term]; 88 | // Binary Search 89 | int left = 0, right = [_autoCorrectEntries count] -1, mid; 90 | while (right >= left) { 91 | mid = (left + right) / 2; 92 | NSDictionary* item = [_autoCorrectEntries objectAtIndex:mid]; 93 | NSComparisonResult comp = [term compare:[item objectForKey:@"replace"]]; 94 | if (comp == NSOrderedDescending) { 95 | left = mid + 1; 96 | } else if (comp == NSOrderedAscending) { 97 | right = mid - 1; 98 | } else { 99 | return [item objectForKey:@"with"]; 100 | } 101 | } 102 | return nil; 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /AvroKeyboard.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 350CECE5159C45790009F0FE /* database.db3 in Resources */ = {isa = PBXBuildFile; fileRef = 350CECD9159C45790009F0FE /* database.db3 */; }; 11 | 350CECE6159C45790009F0FE /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 350CECDB159C45790009F0FE /* FMDatabase.m */; }; 12 | 350CECE7159C45790009F0FE /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = 350CECDD159C45790009F0FE /* FMDatabasePool.m */; }; 13 | 350CECE8159C45790009F0FE /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 350CECDF159C45790009F0FE /* FMResultSet.m */; }; 14 | 350CECE9159C45790009F0FE /* regex.json in Resources */ = {isa = PBXBuildFile; fileRef = 350CECE0159C45790009F0FE /* regex.json */; }; 15 | 350CECEA159C45790009F0FE /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = 350CECE2159C45790009F0FE /* RegexKitLite.m */; }; 16 | 350CECEB159C45790009F0FE /* RegexParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 350CECE4159C45790009F0FE /* RegexParser.m */; }; 17 | 350CECF2159C5BEF0009F0FE /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 350CECF1159C5BEF0009F0FE /* libicucore.dylib */; }; 18 | 350CECF6159C5C5F0009F0FE /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 350CECF5159C5C5F0009F0FE /* libsqlite3.dylib */; }; 19 | 350CED1B159CF3300009F0FE /* Database.m in Sources */ = {isa = PBXBuildFile; fileRef = 350CED18159CF3300009F0FE /* Database.m */; }; 20 | 350CED1C159CF3300009F0FE /* Suggestion.m in Sources */ = {isa = PBXBuildFile; fileRef = 350CED1A159CF3300009F0FE /* Suggestion.m */; }; 21 | 3512ED5A159785400085E81E /* preferences.nib in Resources */ = {isa = PBXBuildFile; fileRef = 3512ED581597853F0085E81E /* preferences.nib */; }; 22 | 3553A33515A0009E00FE8491 /* CacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3553A33415A0009E00FE8491 /* CacheManager.m */; }; 23 | 356B186915A445DA00091215 /* AutoCorrect.png in Resources */ = {isa = PBXBuildFile; fileRef = 356B186815A445DA00091215 /* AutoCorrect.png */; }; 24 | 356B186D15A4499B00091215 /* Credits.png in Resources */ = {isa = PBXBuildFile; fileRef = 356B186B15A4499B00091215 /* Credits.png */; }; 25 | 356B186E15A4499B00091215 /* General.png in Resources */ = {isa = PBXBuildFile; fileRef = 356B186C15A4499B00091215 /* General.png */; }; 26 | 3578E8FD15A26EC100C1FB8E /* Credits.rtfd in Resources */ = {isa = PBXBuildFile; fileRef = 3578E8FC15A26EC100C1FB8E /* Credits.rtfd */; }; 27 | 3587CCD815A7C5E50074BFF1 /* preferences.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3587CCD715A7C5E50074BFF1 /* preferences.plist */; }; 28 | 35895BF01597639A00F2A3ED /* AutoCorrect.m in Sources */ = {isa = PBXBuildFile; fileRef = 35895BEF1597639A00F2A3ED /* AutoCorrect.m */; }; 29 | 35895BF3159763A300F2A3ED /* autodict.dct in Resources */ = {isa = PBXBuildFile; fileRef = 35895BF2159763A300F2A3ED /* autodict.dct */; }; 30 | 35895BFE1597706800F2A3ED /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 35895BFA1597706800F2A3ED /* MainMenu.nib */; }; 31 | 359C1C70159286750080E2FD /* Candidates.m in Sources */ = {isa = PBXBuildFile; fileRef = 359C1C6F159286750080E2FD /* Candidates.m */; }; 32 | 35B007CB1597BF9A000CE409 /* PreferencesController.m in Sources */ = {isa = PBXBuildFile; fileRef = 35B007CA1597BF9A000CE409 /* PreferencesController.m */; }; 33 | 35B47478159A668D00C69C7D /* avro.icns in Resources */ = {isa = PBXBuildFile; fileRef = 35B47477159A668D00C69C7D /* avro.icns */; }; 34 | 35B6EE6D15A0C4F2004C7C9B /* NSString+Levenshtein.m in Sources */ = {isa = PBXBuildFile; fileRef = 35B6EE6C15A0C4F2004C7C9B /* NSString+Levenshtein.m */; }; 35 | 35DBC86B1597A15B00B9772E /* MainMenuAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 35DBC86A1597A15B00B9772E /* MainMenuAppDelegate.m */; }; 36 | 35F4DE73159404A3001EAC81 /* data.json in Resources */ = {isa = PBXBuildFile; fileRef = 35F4DE72159404A3001EAC81 /* data.json */; }; 37 | 35F4DE7F1594096B001EAC81 /* AvroParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 35F4DE7E1594096B001EAC81 /* AvroParser.m */; }; 38 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 39 | 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 40 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 41 | E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E93074B60A5C264700470842 /* InputMethodKit.framework */; }; 42 | E93074E20A5C2F1200470842 /* AvroKeyboardController.m in Sources */ = {isa = PBXBuildFile; fileRef = E93074E10A5C2F1200470842 /* AvroKeyboardController.m */; }; 43 | /* End PBXBuildFile section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 47 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 48 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 49 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 50 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 51 | 32CA4F630368D1EE00C91783 /* AvroKeyboard_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvroKeyboard_Prefix.pch; sourceTree = ""; }; 52 | 350CECD9159C45790009F0FE /* database.db3 */ = {isa = PBXFileReference; lastKnownFileType = file; path = database.db3; sourceTree = ""; }; 53 | 350CECDA159C45790009F0FE /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = ""; }; 54 | 350CECDB159C45790009F0FE /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = ""; }; 55 | 350CECDC159C45790009F0FE /* FMDatabasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabasePool.h; sourceTree = ""; }; 56 | 350CECDD159C45790009F0FE /* FMDatabasePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabasePool.m; sourceTree = ""; }; 57 | 350CECDE159C45790009F0FE /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMResultSet.h; sourceTree = ""; }; 58 | 350CECDF159C45790009F0FE /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMResultSet.m; sourceTree = ""; }; 59 | 350CECE0159C45790009F0FE /* regex.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = regex.json; sourceTree = ""; }; 60 | 350CECE1159C45790009F0FE /* RegexKitLite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegexKitLite.h; sourceTree = ""; }; 61 | 350CECE2159C45790009F0FE /* RegexKitLite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegexKitLite.m; sourceTree = ""; }; 62 | 350CECE3159C45790009F0FE /* RegexParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegexParser.h; sourceTree = ""; }; 63 | 350CECE4159C45790009F0FE /* RegexParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegexParser.m; sourceTree = ""; }; 64 | 350CECF1159C5BEF0009F0FE /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = /usr/lib/libicucore.dylib; sourceTree = ""; }; 65 | 350CECF5159C5C5F0009F0FE /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = /usr/lib/libsqlite3.dylib; sourceTree = ""; }; 66 | 350CED17159CF3300009F0FE /* Database.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Database.h; sourceTree = ""; }; 67 | 350CED18159CF3300009F0FE /* Database.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Database.m; sourceTree = ""; }; 68 | 350CED19159CF3300009F0FE /* Suggestion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Suggestion.h; sourceTree = ""; }; 69 | 350CED1A159CF3300009F0FE /* Suggestion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Suggestion.m; sourceTree = ""; }; 70 | 3512ED591597853F0085E81E /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/preferences.nib; sourceTree = ""; }; 71 | 3553A33315A0009D00FE8491 /* CacheManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CacheManager.h; sourceTree = ""; }; 72 | 3553A33415A0009E00FE8491 /* CacheManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CacheManager.m; sourceTree = ""; }; 73 | 356B186815A445DA00091215 /* AutoCorrect.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = AutoCorrect.png; path = Icons/AutoCorrect.png; sourceTree = ""; }; 74 | 356B186B15A4499B00091215 /* Credits.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Credits.png; path = Icons/Credits.png; sourceTree = ""; }; 75 | 356B186C15A4499B00091215 /* General.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = General.png; path = Icons/General.png; sourceTree = ""; }; 76 | 3578E8FC15A26EC100C1FB8E /* Credits.rtfd */ = {isa = PBXFileReference; lastKnownFileType = wrapper.rtfd; path = Credits.rtfd; sourceTree = ""; }; 77 | 3587CCD715A7C5E50074BFF1 /* preferences.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = preferences.plist; sourceTree = ""; }; 78 | 35895BEE1597639A00F2A3ED /* AutoCorrect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoCorrect.h; sourceTree = ""; }; 79 | 35895BEF1597639A00F2A3ED /* AutoCorrect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AutoCorrect.m; sourceTree = ""; }; 80 | 35895BF2159763A300F2A3ED /* autodict.dct */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = autodict.dct; sourceTree = ""; }; 81 | 35895BFB1597706800F2A3ED /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; 82 | 359C1C6E159286750080E2FD /* Candidates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Candidates.h; sourceTree = ""; }; 83 | 359C1C6F159286750080E2FD /* Candidates.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Candidates.m; sourceTree = ""; }; 84 | 35B007C91597BF9A000CE409 /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesController.h; sourceTree = ""; }; 85 | 35B007CA1597BF9A000CE409 /* PreferencesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreferencesController.m; sourceTree = ""; }; 86 | 35B47477159A668D00C69C7D /* avro.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = avro.icns; sourceTree = ""; }; 87 | 35B6EE6B15A0C4F2004C7C9B /* NSString+Levenshtein.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Levenshtein.h"; sourceTree = ""; }; 88 | 35B6EE6C15A0C4F2004C7C9B /* NSString+Levenshtein.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Levenshtein.m"; sourceTree = ""; }; 89 | 35DBC8691597A15B00B9772E /* MainMenuAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainMenuAppDelegate.h; sourceTree = ""; }; 90 | 35DBC86A1597A15B00B9772E /* MainMenuAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainMenuAppDelegate.m; sourceTree = ""; }; 91 | 35F4DE72159404A3001EAC81 /* data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = data.json; sourceTree = ""; }; 92 | 35F4DE7D1594096B001EAC81 /* AvroParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvroParser.h; sourceTree = ""; }; 93 | 35F4DE7E1594096B001EAC81 /* AvroParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AvroParser.m; sourceTree = ""; }; 94 | 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 95 | 8D1107320486CEB800E47090 /* Avro Keyboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Avro Keyboard.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 96 | E93074B60A5C264700470842 /* InputMethodKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InputMethodKit.framework; path = /System/Library/Frameworks/InputMethodKit.framework; sourceTree = ""; }; 97 | E93074E00A5C2F1200470842 /* AvroKeyboardController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvroKeyboardController.h; sourceTree = ""; }; 98 | E93074E10A5C2F1200470842 /* AvroKeyboardController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AvroKeyboardController.m; sourceTree = ""; }; 99 | /* End PBXFileReference section */ 100 | 101 | /* Begin PBXFrameworksBuildPhase section */ 102 | 8D11072E0486CEB800E47090 /* Frameworks */ = { 103 | isa = PBXFrameworksBuildPhase; 104 | buildActionMask = 2147483647; 105 | files = ( 106 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 107 | E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */, 108 | 350CECF6159C5C5F0009F0FE /* libsqlite3.dylib in Frameworks */, 109 | 350CECF2159C5BEF0009F0FE /* libicucore.dylib in Frameworks */, 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | /* End PBXFrameworksBuildPhase section */ 114 | 115 | /* Begin PBXGroup section */ 116 | 080E96DDFE201D6D7F000001 /* Classes */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 35B007C51597BF56000CE409 /* Controllers */, 120 | 35B007CC1597BFC3000CE409 /* Others */, 121 | 35B6EE6915A0C4AD004C7C9B /* Libraries */, 122 | 35DBC8691597A15B00B9772E /* MainMenuAppDelegate.h */, 123 | 35DBC86A1597A15B00B9772E /* MainMenuAppDelegate.m */, 124 | ); 125 | name = Classes; 126 | sourceTree = ""; 127 | }; 128 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, 132 | ); 133 | name = "Linked Frameworks"; 134 | sourceTree = ""; 135 | }; 136 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | E93074B60A5C264700470842 /* InputMethodKit.framework */, 140 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */, 141 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */, 142 | ); 143 | name = "Other Frameworks"; 144 | sourceTree = ""; 145 | }; 146 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 8D1107320486CEB800E47090 /* Avro Keyboard.app */, 150 | ); 151 | name = Products; 152 | sourceTree = ""; 153 | }; 154 | 29B97314FDCFA39411CA2CEA /* NumberInput */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 080E96DDFE201D6D7F000001 /* Classes */, 158 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 159 | 29B97317FDCFA39411CA2CEA /* Resources */, 160 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 161 | 19C28FACFE9D520D11CA2CBB /* Products */, 162 | ); 163 | name = NumberInput; 164 | sourceTree = ""; 165 | }; 166 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 32CA4F630368D1EE00C91783 /* AvroKeyboard_Prefix.pch */, 170 | 29B97316FDCFA39411CA2CEA /* main.m */, 171 | ); 172 | name = "Other Sources"; 173 | sourceTree = ""; 174 | }; 175 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | 356B186115A4434D00091215 /* Icons */, 179 | 35B47477159A668D00C69C7D /* avro.icns */, 180 | 35895BF2159763A300F2A3ED /* autodict.dct */, 181 | 35F4DE72159404A3001EAC81 /* data.json */, 182 | 350CECE0159C45790009F0FE /* regex.json */, 183 | 350CECD9159C45790009F0FE /* database.db3 */, 184 | 35895BFA1597706800F2A3ED /* MainMenu.nib */, 185 | 3512ED581597853F0085E81E /* preferences.nib */, 186 | 3587CCD715A7C5E50074BFF1 /* preferences.plist */, 187 | 8D1107310486CEB800E47090 /* Info.plist */, 188 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 189 | 3578E8FC15A26EC100C1FB8E /* Credits.rtfd */, 190 | ); 191 | name = Resources; 192 | sourceTree = ""; 193 | }; 194 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 198 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, 199 | 350CECF5159C5C5F0009F0FE /* libsqlite3.dylib */, 200 | 350CECF1159C5BEF0009F0FE /* libicucore.dylib */, 201 | ); 202 | name = Frameworks; 203 | sourceTree = ""; 204 | }; 205 | 350CECED159C45870009F0FE /* FMDatabase */ = { 206 | isa = PBXGroup; 207 | children = ( 208 | 350CECDA159C45790009F0FE /* FMDatabase.h */, 209 | 350CECDB159C45790009F0FE /* FMDatabase.m */, 210 | 350CECDC159C45790009F0FE /* FMDatabasePool.h */, 211 | 350CECDD159C45790009F0FE /* FMDatabasePool.m */, 212 | 350CECDE159C45790009F0FE /* FMResultSet.h */, 213 | 350CECDF159C45790009F0FE /* FMResultSet.m */, 214 | ); 215 | name = FMDatabase; 216 | sourceTree = ""; 217 | }; 218 | 350CECEE159C45C50009F0FE /* RegexKit */ = { 219 | isa = PBXGroup; 220 | children = ( 221 | 350CECE1159C45790009F0FE /* RegexKitLite.h */, 222 | 350CECE2159C45790009F0FE /* RegexKitLite.m */, 223 | ); 224 | name = RegexKit; 225 | sourceTree = ""; 226 | }; 227 | 356B186115A4434D00091215 /* Icons */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | 356B186B15A4499B00091215 /* Credits.png */, 231 | 356B186C15A4499B00091215 /* General.png */, 232 | 356B186815A445DA00091215 /* AutoCorrect.png */, 233 | ); 234 | name = Icons; 235 | sourceTree = ""; 236 | }; 237 | 35B007C51597BF56000CE409 /* Controllers */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | E93074E00A5C2F1200470842 /* AvroKeyboardController.h */, 241 | E93074E10A5C2F1200470842 /* AvroKeyboardController.m */, 242 | 35B007C91597BF9A000CE409 /* PreferencesController.h */, 243 | 35B007CA1597BF9A000CE409 /* PreferencesController.m */, 244 | ); 245 | name = Controllers; 246 | sourceTree = ""; 247 | }; 248 | 35B007CC1597BFC3000CE409 /* Others */ = { 249 | isa = PBXGroup; 250 | children = ( 251 | 35B6EE6815A0C4A4004C7C9B /* Parsers */, 252 | 350CED19159CF3300009F0FE /* Suggestion.h */, 253 | 350CED1A159CF3300009F0FE /* Suggestion.m */, 254 | 3553A33315A0009D00FE8491 /* CacheManager.h */, 255 | 3553A33415A0009E00FE8491 /* CacheManager.m */, 256 | 359C1C6E159286750080E2FD /* Candidates.h */, 257 | 359C1C6F159286750080E2FD /* Candidates.m */, 258 | ); 259 | name = Others; 260 | sourceTree = ""; 261 | }; 262 | 35B6EE6815A0C4A4004C7C9B /* Parsers */ = { 263 | isa = PBXGroup; 264 | children = ( 265 | 35895BEE1597639A00F2A3ED /* AutoCorrect.h */, 266 | 35895BEF1597639A00F2A3ED /* AutoCorrect.m */, 267 | 35F4DE7D1594096B001EAC81 /* AvroParser.h */, 268 | 35F4DE7E1594096B001EAC81 /* AvroParser.m */, 269 | 350CECE3159C45790009F0FE /* RegexParser.h */, 270 | 350CECE4159C45790009F0FE /* RegexParser.m */, 271 | 350CED17159CF3300009F0FE /* Database.h */, 272 | 350CED18159CF3300009F0FE /* Database.m */, 273 | ); 274 | name = Parsers; 275 | sourceTree = ""; 276 | }; 277 | 35B6EE6915A0C4AD004C7C9B /* Libraries */ = { 278 | isa = PBXGroup; 279 | children = ( 280 | 350CECED159C45870009F0FE /* FMDatabase */, 281 | 350CECEE159C45C50009F0FE /* RegexKit */, 282 | 35B6EE6B15A0C4F2004C7C9B /* NSString+Levenshtein.h */, 283 | 35B6EE6C15A0C4F2004C7C9B /* NSString+Levenshtein.m */, 284 | ); 285 | name = Libraries; 286 | sourceTree = ""; 287 | }; 288 | /* End PBXGroup section */ 289 | 290 | /* Begin PBXNativeTarget section */ 291 | 8D1107260486CEB800E47090 /* Avro Keyboard */ = { 292 | isa = PBXNativeTarget; 293 | buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Avro Keyboard" */; 294 | buildPhases = ( 295 | 8D1107290486CEB800E47090 /* Resources */, 296 | 8D11072C0486CEB800E47090 /* Sources */, 297 | 8D11072E0486CEB800E47090 /* Frameworks */, 298 | ); 299 | buildRules = ( 300 | ); 301 | dependencies = ( 302 | ); 303 | name = "Avro Keyboard"; 304 | productInstallPath = "$(HOME)/Applications"; 305 | productName = NumberInput; 306 | productReference = 8D1107320486CEB800E47090 /* Avro Keyboard.app */; 307 | productType = "com.apple.product-type.application"; 308 | }; 309 | /* End PBXNativeTarget section */ 310 | 311 | /* Begin PBXProject section */ 312 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 313 | isa = PBXProject; 314 | attributes = { 315 | LastUpgradeCheck = 0430; 316 | }; 317 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "AvroKeyboard" */; 318 | compatibilityVersion = "Xcode 3.2"; 319 | developmentRegion = English; 320 | hasScannedForEncodings = 1; 321 | knownRegions = ( 322 | en, 323 | English, 324 | ); 325 | mainGroup = 29B97314FDCFA39411CA2CEA /* NumberInput */; 326 | projectDirPath = ""; 327 | projectRoot = ""; 328 | targets = ( 329 | 8D1107260486CEB800E47090 /* Avro Keyboard */, 330 | ); 331 | }; 332 | /* End PBXProject section */ 333 | 334 | /* Begin PBXResourcesBuildPhase section */ 335 | 8D1107290486CEB800E47090 /* Resources */ = { 336 | isa = PBXResourcesBuildPhase; 337 | buildActionMask = 2147483647; 338 | files = ( 339 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, 340 | 35F4DE73159404A3001EAC81 /* data.json in Resources */, 341 | 35895BF3159763A300F2A3ED /* autodict.dct in Resources */, 342 | 35895BFE1597706800F2A3ED /* MainMenu.nib in Resources */, 343 | 3512ED5A159785400085E81E /* preferences.nib in Resources */, 344 | 35B47478159A668D00C69C7D /* avro.icns in Resources */, 345 | 350CECE5159C45790009F0FE /* database.db3 in Resources */, 346 | 350CECE9159C45790009F0FE /* regex.json in Resources */, 347 | 3578E8FD15A26EC100C1FB8E /* Credits.rtfd in Resources */, 348 | 356B186915A445DA00091215 /* AutoCorrect.png in Resources */, 349 | 356B186D15A4499B00091215 /* Credits.png in Resources */, 350 | 356B186E15A4499B00091215 /* General.png in Resources */, 351 | 3587CCD815A7C5E50074BFF1 /* preferences.plist in Resources */, 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | }; 355 | /* End PBXResourcesBuildPhase section */ 356 | 357 | /* Begin PBXSourcesBuildPhase section */ 358 | 8D11072C0486CEB800E47090 /* Sources */ = { 359 | isa = PBXSourcesBuildPhase; 360 | buildActionMask = 2147483647; 361 | files = ( 362 | 8D11072D0486CEB800E47090 /* main.m in Sources */, 363 | E93074E20A5C2F1200470842 /* AvroKeyboardController.m in Sources */, 364 | 359C1C70159286750080E2FD /* Candidates.m in Sources */, 365 | 35F4DE7F1594096B001EAC81 /* AvroParser.m in Sources */, 366 | 35895BF01597639A00F2A3ED /* AutoCorrect.m in Sources */, 367 | 35DBC86B1597A15B00B9772E /* MainMenuAppDelegate.m in Sources */, 368 | 35B007CB1597BF9A000CE409 /* PreferencesController.m in Sources */, 369 | 350CECE6159C45790009F0FE /* FMDatabase.m in Sources */, 370 | 350CECE7159C45790009F0FE /* FMDatabasePool.m in Sources */, 371 | 350CECE8159C45790009F0FE /* FMResultSet.m in Sources */, 372 | 350CECEA159C45790009F0FE /* RegexKitLite.m in Sources */, 373 | 350CECEB159C45790009F0FE /* RegexParser.m in Sources */, 374 | 350CED1B159CF3300009F0FE /* Database.m in Sources */, 375 | 350CED1C159CF3300009F0FE /* Suggestion.m in Sources */, 376 | 3553A33515A0009E00FE8491 /* CacheManager.m in Sources */, 377 | 35B6EE6D15A0C4F2004C7C9B /* NSString+Levenshtein.m in Sources */, 378 | ); 379 | runOnlyForDeploymentPostprocessing = 0; 380 | }; 381 | /* End PBXSourcesBuildPhase section */ 382 | 383 | /* Begin PBXVariantGroup section */ 384 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { 385 | isa = PBXVariantGroup; 386 | children = ( 387 | 089C165DFE840E0CC02AAC07 /* English */, 388 | ); 389 | name = InfoPlist.strings; 390 | sourceTree = ""; 391 | }; 392 | 3512ED581597853F0085E81E /* preferences.nib */ = { 393 | isa = PBXVariantGroup; 394 | children = ( 395 | 3512ED591597853F0085E81E /* English */, 396 | ); 397 | name = preferences.nib; 398 | sourceTree = ""; 399 | }; 400 | 35895BFA1597706800F2A3ED /* MainMenu.nib */ = { 401 | isa = PBXVariantGroup; 402 | children = ( 403 | 35895BFB1597706800F2A3ED /* English */, 404 | ); 405 | name = MainMenu.nib; 406 | sourceTree = ""; 407 | }; 408 | /* End PBXVariantGroup section */ 409 | 410 | /* Begin XCBuildConfiguration section */ 411 | C01FCF4B08A954540054247B /* Debug */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 415 | COPY_PHASE_STRIP = NO; 416 | GCC_DYNAMIC_NO_PIC = NO; 417 | GCC_MODEL_TUNING = G5; 418 | GCC_OPTIMIZATION_LEVEL = 0; 419 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 420 | INFOPLIST_FILE = Info.plist; 421 | INSTALL_PATH = "\"$(LOCAL_LIBRARY_DIR)/Input Methods/\""; 422 | PRODUCT_NAME = "Avro Keyboard"; 423 | WRAPPER_EXTENSION = app; 424 | ZERO_LINK = YES; 425 | }; 426 | name = Debug; 427 | }; 428 | C01FCF4C08A954540054247B /* Release */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 432 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 433 | GCC_MODEL_TUNING = G5; 434 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 435 | INFOPLIST_FILE = Info.plist; 436 | INSTALL_PATH = "$(HOME)/Applications"; 437 | PRODUCT_NAME = "Avro Keyboard"; 438 | WRAPPER_EXTENSION = app; 439 | }; 440 | name = Release; 441 | }; 442 | C01FCF4F08A954540054247B /* Debug */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 446 | GCC_WARN_UNUSED_VARIABLE = YES; 447 | }; 448 | name = Debug; 449 | }; 450 | C01FCF5008A954540054247B /* Release */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 454 | GCC_WARN_UNUSED_VARIABLE = YES; 455 | }; 456 | name = Release; 457 | }; 458 | /* End XCBuildConfiguration section */ 459 | 460 | /* Begin XCConfigurationList section */ 461 | C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Avro Keyboard" */ = { 462 | isa = XCConfigurationList; 463 | buildConfigurations = ( 464 | C01FCF4B08A954540054247B /* Debug */, 465 | C01FCF4C08A954540054247B /* Release */, 466 | ); 467 | defaultConfigurationIsVisible = 0; 468 | defaultConfigurationName = Release; 469 | }; 470 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "AvroKeyboard" */ = { 471 | isa = XCConfigurationList; 472 | buildConfigurations = ( 473 | C01FCF4F08A954540054247B /* Debug */, 474 | C01FCF5008A954540054247B /* Release */, 475 | ); 476 | defaultConfigurationIsVisible = 0; 477 | defaultConfigurationName = Release; 478 | }; 479 | /* End XCConfigurationList section */ 480 | }; 481 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 482 | } 483 | -------------------------------------------------------------------------------- /AvroKeyboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AvroKeyboardController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/21/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | 12 | @interface AvroKeyboardController : IMKInputController { 13 | @private 14 | id _currentClient; // the current active client. 15 | int _prevSelected; 16 | NSMutableString* _composedBuffer; // _composedBuffer contains text that the input method has converted 17 | NSMutableArray* _currentCandidates; 18 | NSString* _prefix; // Converted Prefix 19 | NSString* _term; // Original Non-Converted Term 20 | NSString* _suffix; // Converted Suffix 21 | } 22 | 23 | @property (nonatomic, retain) NSString* prefix; 24 | @property (nonatomic, retain) NSString* term; 25 | @property (nonatomic, retain) NSString* suffix; 26 | 27 | //These are simple methods for managing our composition and original buffers 28 | //They are all simple wrappers around basic NSString methods. 29 | - (void)commitText:(NSString*)string; 30 | 31 | @end -------------------------------------------------------------------------------- /AvroKeyboardController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/21/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "AvroKeyboardController.h" 9 | #import "Suggestion.h" 10 | #import "Candidates.h" 11 | #import "CacheManager.h" 12 | #import "RegexKitLite.h" 13 | #import "AvroParser.h" 14 | #import "AutoCorrect.h" 15 | 16 | @implementation AvroKeyboardController 17 | 18 | @synthesize prefix = _prefix, term = _term, suffix = _suffix; 19 | 20 | - (id)initWithServer:(IMKServer*)server delegate:(id)delegate client:(id)inputClient { 21 | 22 | self = [super initWithServer:server delegate:delegate client:inputClient]; 23 | 24 | if (self) { 25 | _currentClient = inputClient; 26 | _composedBuffer = [[NSMutableString alloc] initWithString:@""]; 27 | _currentCandidates = [[NSMutableArray alloc] initWithCapacity:0]; 28 | _prevSelected = -1; 29 | } 30 | 31 | return self; 32 | } 33 | 34 | - (void)dealloc { 35 | [_prefix release]; 36 | [_term release]; 37 | [_suffix release]; 38 | [_currentCandidates release]; 39 | [_composedBuffer release]; 40 | [super dealloc]; 41 | } 42 | 43 | - (void)findCurrentCandidates { 44 | [_currentCandidates removeAllObjects]; 45 | if (_composedBuffer && [_composedBuffer length] > 0) { 46 | NSString* regex = @"(^(?::`|\\.`|[-\\]\\\\~!@#&*()_=+\\[{}'\";<>/?|.,])*?(?=(?:,{2,}))|^(?::`|\\.`|[-\\]\\\\~!@#&*()_=+\\[{}'\";<>/?|.,])*)(.*?(?:,,)*)((?::`|\\.`|[-\\]\\\\~!@#&*()_=+\\[{}'\";<>/?|.,])*$)"; 47 | NSArray* items = [_composedBuffer captureComponentsMatchedByRegex:regex]; 48 | if (items && [items count] > 0) { 49 | // Split Prefix, Term & Suffix 50 | [self setPrefix:[[AvroParser sharedInstance] parse:[items objectAtIndex:1]]]; 51 | [self setTerm:[items objectAtIndex:2]]; 52 | [self setSuffix:[[AvroParser sharedInstance] parse:[items objectAtIndex:3]]]; 53 | 54 | _currentCandidates = [[[Suggestion sharedInstance] getList:[self term]] retain]; 55 | if (_currentCandidates && [_currentCandidates count] > 0) { 56 | NSString* prevString = nil; 57 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeDictionary"]) { 58 | _prevSelected = -1; 59 | prevString = [[CacheManager sharedInstance] stringForKey:[self term]]; 60 | } 61 | int i; 62 | for (i = 0; i < [_currentCandidates count]; ++i) { 63 | NSString* item = [_currentCandidates objectAtIndex:i]; 64 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeDictionary"] && 65 | _prevSelected && [item isEqualToString:prevString] ) { 66 | _prevSelected = i; 67 | } 68 | [_currentCandidates replaceObjectAtIndex:i withObject: 69 | [NSString stringWithFormat:@"%@%@%@", [self prefix], item, [self suffix]]]; 70 | } 71 | // Emoticons 72 | if ([_composedBuffer isEqualToString:[self term]] == NO && 73 | [[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeDictionary"]) { 74 | NSString* smily = [[AutoCorrect sharedInstance] find:_composedBuffer]; 75 | if (smily) { 76 | [_currentCandidates insertObject:smily atIndex:0]; 77 | } 78 | } 79 | } 80 | else { 81 | [_currentCandidates addObject:[self prefix]]; 82 | } 83 | } 84 | } 85 | } 86 | 87 | - (void)updateCandidatesPanel { 88 | if (_currentCandidates && [_currentCandidates count] > 0) { 89 | NSUserDefaults *defaultsDictionary = [NSUserDefaults standardUserDefaults]; 90 | 91 | // NSString *candidateFontName = [defaultsDictionary objectForKey:@"candidateFontName"]; 92 | // float candidateFontSize = [[defaultsDictionary objectForKey:@"candidateFontSize"] floatValue]; 93 | 94 | // NSFont *candidateFont = [NSFont fontWithName:candidateFontName size:candidateFontSize]; 95 | // [[Candidates sharedInstance] setAttributes:[NSDictionary dictionaryWithObject:candidateFont forKey:NSFontAttributeName]]; 96 | 97 | [[Candidates sharedInstance] setPanelType:[defaultsDictionary integerForKey:@"CandidatePanelType"]]; 98 | [[Candidates sharedInstance] updateCandidates]; 99 | [[Candidates sharedInstance] show:kIMKLocateCandidatesBelowHint]; 100 | if (_prevSelected > -1) { 101 | [[Candidates sharedInstance] selectCandidate:_prevSelected]; 102 | } 103 | } 104 | else { 105 | [[Candidates sharedInstance] hide]; 106 | } 107 | } 108 | 109 | - (NSArray*)candidates:(id)sender { 110 | return _currentCandidates; 111 | } 112 | 113 | - (void)candidateSelectionChanged:(NSAttributedString*)candidateString { 114 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeDictionary"]) { 115 | if ([self term] && [[self term] length] > 0) { 116 | BOOL comp = [[candidateString string] isEqualToString:[_currentCandidates objectAtIndex:0]]; 117 | if ((comp && _prevSelected == -1) == NO) { 118 | NSRange range = NSMakeRange([[self prefix] length], 119 | [candidateString length] - ([[self prefix] length] + [[self suffix] length])); 120 | [[CacheManager sharedInstance] setString:[[candidateString string] substringWithRange:range] forKey:[self term]]; 121 | 122 | // Reverse Suffix Caching 123 | NSArray* tmpArray = [[CacheManager sharedInstance] baseForKey:[candidateString string]]; 124 | if (tmpArray && [tmpArray count] > 0) { 125 | [[CacheManager sharedInstance] setString:[tmpArray objectAtIndex:1] forKey:[tmpArray objectAtIndex:0]]; 126 | } 127 | } 128 | } 129 | } 130 | } 131 | 132 | - (void)candidateSelected:(NSAttributedString*)candidateString { 133 | [_currentClient insertText:candidateString replacementRange:NSMakeRange(NSNotFound, 0)]; 134 | 135 | [self clearCompositionBuffer]; 136 | [_currentCandidates removeAllObjects]; 137 | [self updateCandidatesPanel]; 138 | } 139 | 140 | - (void)commitComposition:(id)sender { 141 | [sender insertText:_composedBuffer replacementRange:NSMakeRange(NSNotFound, 0)]; 142 | 143 | [self clearCompositionBuffer]; 144 | [_currentCandidates removeAllObjects]; 145 | [self updateCandidatesPanel]; 146 | } 147 | 148 | - (id)composedString:(id)sender { 149 | return [[[NSAttributedString alloc] initWithString:_composedBuffer] autorelease]; 150 | } 151 | 152 | - (void)clearCompositionBuffer { 153 | [_composedBuffer deleteCharactersInRange:NSMakeRange(0, [_composedBuffer length])]; 154 | } 155 | 156 | /* 157 | Implement one of the three ways to receive input from the client. 158 | Here are the three approaches: 159 | 160 | 1. Support keybinding. 161 | In this approach the system takes each keydown and trys to map the keydown to an action method that the input method has implemented. If an action is found the system calls didCommandBySelector:client:. If no action method is found inputText:client: is called. An input method choosing this approach should implement 162 | -(BOOL)inputText:(NSString*)string client:(id)sender; 163 | -(BOOL)didCommandBySelector:(SEL)aSelector client:(id)sender; 164 | 165 | 2. Receive all key events without the keybinding, but do "unpack" the relevant text data. 166 | Key events are broken down into the Unicodes, the key code that generated them, and modifier flags. This data is then sent to the input method's inputText:key:modifiers:client: method. For this approach implement: 167 | -(BOOL)inputText:(NSString*)string key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)sender; 168 | 169 | 3. Receive events directly from the Text Services Manager as NSEvent objects. For this approach implement: 170 | -(BOOL)handleEvent:(NSEvent*)event client:(id)sender; 171 | */ 172 | 173 | /*! 174 | @method 175 | @abstract Receive incoming text. 176 | @discussion This method receives key board input from the client application. The method receives the key input as an NSString. The string will have been created from the keydown event by the InputMethodKit. 177 | */ 178 | - (BOOL)inputText:(NSString*)string client:(id)sender { 179 | // Return YES to indicate the the key input was received and dealt with. Key processing will not continue in that case. In 180 | // other words the system will not deliver a key down event to the application. 181 | // Returning NO means the original key down will be passed on to the client. 182 | if ([string isEqualToString:@" "]) { 183 | if (_currentCandidates && [_currentCandidates count]) { 184 | [self candidateSelected:[[Candidates sharedInstance] selectedCandidateString]]; 185 | } 186 | return NO; 187 | } 188 | else { 189 | [_composedBuffer appendString:string]; 190 | [self findCurrentCandidates]; 191 | [self updateComposition]; 192 | [self updateCandidatesPanel]; 193 | return YES; 194 | } 195 | } 196 | 197 | - (void)deleteBackward:(id)sender { 198 | // We're called only when [compositionBuffer length] > 0 199 | [_composedBuffer deleteCharactersInRange:NSMakeRange([_composedBuffer length] - 1, 1)]; 200 | [self findCurrentCandidates]; 201 | [self updateComposition]; 202 | [self updateCandidatesPanel]; 203 | } 204 | 205 | - (void)insertTab:(id)sender { 206 | [self commitText:@"\t"]; 207 | } 208 | 209 | - (void)insertNewline:(id)sender { 210 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"CommitNewLineOnEnter"]) { 211 | [self commitText:@"\n"]; 212 | } 213 | else { 214 | [self commitText:@""]; 215 | } 216 | } 217 | 218 | - (BOOL)didCommandBySelector:(SEL)aSelector client:(id)sender { 219 | if ([self respondsToSelector:aSelector]) { 220 | // The NSResponder methods like insertNewline: or deleteBackward: are 221 | // methods that return void. didCommandBySelector method requires 222 | // that you return YES if the command is handled and NO if you do not. 223 | // This is necessary so that unhandled commands can be passed on to the 224 | // client application. For that reason we need to test in the case where 225 | // we might not handle the command. 226 | 227 | if (_composedBuffer && [_composedBuffer length] > 0) { 228 | if (aSelector == @selector(insertTab:) 229 | || aSelector == @selector(insertNewline:) 230 | || aSelector == @selector(deleteBackward:)) { 231 | [self performSelector:aSelector withObject:sender]; 232 | return YES; 233 | } 234 | } 235 | } 236 | return NO; 237 | } 238 | 239 | - (void)commitText:(NSString*)string { 240 | if (_currentCandidates) { 241 | [self candidateSelected:[[Candidates sharedInstance] selectedCandidateString]]; 242 | [_currentClient insertText:string replacementRange:NSMakeRange(NSNotFound, 0)]; 243 | } 244 | else { 245 | NSBeep(); 246 | } 247 | } 248 | 249 | - (NSMenu*)menu { 250 | return [[NSApp delegate] menu]; 251 | } 252 | 253 | @end -------------------------------------------------------------------------------- /AvroKeyboard_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/21/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #ifdef __OBJC__ 9 | #import 10 | #endif 11 | -------------------------------------------------------------------------------- /AvroParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/22/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface AvroParser : NSObject { 11 | NSString* _vowel; 12 | NSString* _consonant; 13 | NSString* _casesensitive; 14 | NSArray* _patterns; 15 | int _maxPatternLength; 16 | } 17 | 18 | + (AvroParser *)sharedInstance; 19 | 20 | - (NSString*)parse:(NSString*)string; 21 | - (NSString*)fix:(NSString*)string; 22 | - (BOOL)isVowel:(unichar)c; 23 | - (BOOL)isConsonant:(unichar)c; 24 | - (BOOL)isPunctuation:(unichar)c; 25 | - (BOOL)isCaseSensitive:(unichar)c; 26 | - (BOOL)isExact:(NSString*) needle heystack:(NSString*)heystack start:(int)start end:(int)end not:(BOOL)not; 27 | - (unichar)smallCap:(unichar) letter; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /AvroParser.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/22/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "AvroParser.h" 9 | 10 | static AvroParser* sharedInstance = nil; 11 | 12 | @implementation AvroParser 13 | 14 | + (AvroParser *)sharedInstance { 15 | if (sharedInstance == nil) { 16 | [[self alloc] init]; // assignment not done here, see allocWithZone 17 | } 18 | return sharedInstance; 19 | } 20 | 21 | + (id)allocWithZone:(NSZone *)zone { 22 | if (sharedInstance == nil) { 23 | sharedInstance = [super allocWithZone:zone]; 24 | return sharedInstance; // assignment and return on first allocation 25 | } 26 | return nil; //on subsequent allocation attempts return nil 27 | } 28 | 29 | - (id)copyWithZone:(NSZone *)zone { 30 | return self; 31 | } 32 | 33 | - (id)retain { 34 | return self; 35 | } 36 | 37 | - (oneway void)release { 38 | //do nothing 39 | } 40 | 41 | - (id)autorelease { 42 | return self; 43 | } 44 | 45 | - (NSUInteger)retainCount { 46 | return NSUIntegerMax; // This is sooo not zero 47 | } 48 | 49 | - (id)init { 50 | self = [super init]; 51 | if (self) { 52 | NSError *error = nil; 53 | NSString *filePath = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"]; 54 | NSData *jsonData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingUncached error: &error]; 55 | 56 | if (jsonData) { 57 | 58 | NSDictionary *jsonArray = [NSJSONSerialization JSONObjectWithData: jsonData options: NSJSONReadingMutableContainers error: &error]; 59 | 60 | if (!jsonArray) { 61 | @throw error; 62 | // @throw [NSException exceptionWithName:@"AvroParser init" reason:@"Error parsing JSON" userInfo:nil]; 63 | } else { 64 | _vowel = [[NSString alloc] initWithString:[jsonArray objectForKey:@"vowel"]]; 65 | _consonant = [[NSString alloc] initWithString:[jsonArray objectForKey:@"consonant"]]; 66 | _casesensitive = [[NSString alloc] initWithString:[jsonArray objectForKey:@"casesensitive"]]; 67 | _patterns = [[NSArray alloc] initWithArray:[jsonArray objectForKey:@"patterns"]]; 68 | _maxPatternLength = [[[_patterns objectAtIndex:0] objectForKey:@"find"] length]; 69 | } 70 | 71 | } else { 72 | @throw error; 73 | } 74 | } 75 | return self; 76 | } 77 | 78 | - (void)dealloc { 79 | [_vowel release]; 80 | [_consonant release]; 81 | [_casesensitive release]; 82 | [_patterns release]; 83 | 84 | [super dealloc]; 85 | } 86 | 87 | - (NSString*)parse:(NSString *)string { 88 | if (!string || [string length] == 0) { 89 | return @""; 90 | } 91 | 92 | NSString * fixed = [self fix:string]; 93 | NSMutableString* output = [[NSMutableString alloc] initWithCapacity:0]; 94 | 95 | int len = [fixed length], cur; 96 | for(cur = 0; cur < len; ++cur) { 97 | int start = cur, end; 98 | BOOL matched = FALSE; 99 | 100 | int chunkLen; 101 | for(chunkLen = _maxPatternLength; chunkLen > 0; --chunkLen) { 102 | end = start + chunkLen; 103 | if(end <= len) { 104 | NSString* chunk = [fixed substringWithRange:NSMakeRange(start, chunkLen)]; 105 | 106 | // Binary Search 107 | int left = 0, right = [_patterns count] - 1, mid; 108 | while(right >= left) { 109 | mid = (right + left) / 2; 110 | NSDictionary* pattern = [_patterns objectAtIndex:mid]; 111 | NSString* find = [pattern objectForKey:@"find"]; 112 | if([find isEqualToString:chunk]) { 113 | NSArray* rules = [pattern objectForKey:@"rules"]; 114 | for(NSDictionary* rule in rules) { 115 | 116 | BOOL replace = TRUE; 117 | int chk = 0; 118 | NSArray* matches = [rule objectForKey:@"matches"]; 119 | for(NSDictionary* match in matches) { 120 | NSString* value = [match objectForKey:@"value"]; 121 | NSString* type = [match objectForKey:@"type"]; 122 | NSString* scope = [match objectForKey:@"scope"]; 123 | BOOL isNegative = [[match objectForKey:@"negative"] boolValue]; 124 | 125 | if([type isEqualToString:@"suffix"]) { 126 | chk = end; 127 | } 128 | // Prefix 129 | else { 130 | chk = start - 1; 131 | } 132 | 133 | // Beginning 134 | if([scope isEqualToString:@"punctuation"]) { 135 | if( 136 | ! ( 137 | (chk < 0 && [type isEqualToString:@"prefix"]) || 138 | (chk >= len && [type isEqualToString:@"suffix"]) || 139 | [self isPunctuation:[fixed characterAtIndex:chk]] 140 | ) ^ isNegative 141 | ) { 142 | replace = FALSE; 143 | break; 144 | } 145 | } 146 | // Vowel 147 | else if([scope isEqualToString:@"vowel"]) { 148 | if( 149 | ! ( 150 | ( 151 | (chk >= 0 && [type isEqualToString:@"prefix"]) || 152 | (chk < len && [type isEqualToString:@"suffix"]) 153 | ) && 154 | [self isVowel:[fixed characterAtIndex:chk]] 155 | ) ^ isNegative 156 | ) { 157 | replace = FALSE; 158 | break; 159 | } 160 | } 161 | // Consonant 162 | else if([scope isEqualToString:@"consonant"]) { 163 | if( 164 | ! ( 165 | ( 166 | (chk >= 0 && [type isEqualToString:@"prefix"]) || 167 | (chk < len && [type isEqualToString:@"suffix"]) 168 | ) && 169 | [self isConsonant:[fixed characterAtIndex:chk]] 170 | ) ^ isNegative 171 | ) { 172 | replace = FALSE; 173 | break; 174 | } 175 | } 176 | // Exact 177 | else if([scope isEqualToString:@"exact"]) { 178 | int s, e; 179 | if([type isEqualToString:@"suffix"]) { 180 | s = end; 181 | e = end + [value length]; 182 | } 183 | // Prefix 184 | else { 185 | s = start - [value length]; 186 | e = start; 187 | } 188 | if(![self isExact:value heystack:fixed start:s end:e not:isNegative]) { 189 | replace = FALSE; 190 | break; 191 | } 192 | } 193 | } 194 | 195 | if(replace) { 196 | [output appendString:[rule objectForKey:@"replace"]]; 197 | cur = end - 1; 198 | matched = TRUE; 199 | break; 200 | } 201 | 202 | } 203 | 204 | if(matched == TRUE) break; 205 | 206 | // Default 207 | [output appendString:[pattern objectForKey:@"replace"]]; 208 | cur = end - 1; 209 | matched = TRUE; 210 | break; 211 | } 212 | else if ([find length] > [chunk length] || 213 | ([find length] == [chunk length] && [find compare:chunk] == NSOrderedAscending)) { 214 | left = mid + 1; 215 | } else { 216 | right = mid - 1; 217 | } 218 | } 219 | if(matched == TRUE) break; 220 | } 221 | } 222 | 223 | if(!matched) { 224 | unichar oldChar = [fixed characterAtIndex:cur]; 225 | [output appendString:[NSString stringWithCharacters:&oldChar length:1]]; 226 | } 227 | // NSLog(@"cur: %s, start: %s, end: %s, prev: %s\n", cur, start, end, prev); 228 | } 229 | 230 | [output autorelease]; 231 | 232 | return output; 233 | } 234 | 235 | - (BOOL)isVowel:(unichar)c { 236 | // Making it lowercase for checking 237 | c = [self smallCap:c]; 238 | int i, len = [_vowel length]; 239 | for (i = 0; i < len; ++i) { 240 | if ([_vowel characterAtIndex:i] == c) { 241 | return TRUE; 242 | } 243 | } 244 | return FALSE; 245 | } 246 | 247 | - (BOOL)isConsonant:(unichar)c { 248 | // Making it lowercase for checking 249 | c = [self smallCap:c]; 250 | int i, len = [_consonant length]; 251 | for (i = 0; i < len; ++i) { 252 | if ([_consonant characterAtIndex:i] == c) { 253 | return TRUE; 254 | } 255 | } 256 | return FALSE; 257 | //return [consonant rangeOfString:c options:NSCaseInsensitiveSearch].location != NSNotFound; 258 | } 259 | 260 | - (BOOL)isPunctuation:(unichar)c { 261 | return !([self isVowel:c] || [self isConsonant:c]); 262 | } 263 | 264 | - (BOOL)isCaseSensitive:(unichar)c { 265 | // Making it lowercase for checking 266 | c = [self smallCap:c]; 267 | int i, len = [_casesensitive length]; 268 | for (i = 0; i < len; ++i) { 269 | if ([_casesensitive characterAtIndex:i] == c) { 270 | return TRUE; 271 | } 272 | } 273 | return FALSE; 274 | } 275 | 276 | - (BOOL)isExact:(NSString*) needle heystack:(NSString*)heystack start:(int)start end:(int)end not:(BOOL)not { 277 | // NSLog(@"Cut: %@", [heystack substringWithRange:NSMakeRange(start, end)]); 278 | int len = end - start; 279 | return ((start >= 0 && end < [heystack length] 280 | && [[heystack substringWithRange:NSMakeRange(start, len)] isEqualToString:needle]) ^ not); 281 | } 282 | 283 | - (unichar)smallCap:(unichar) letter { 284 | if(letter >= 'A' && letter <= 'Z') { 285 | letter = letter - 'A' + 'a'; 286 | } 287 | return letter; 288 | } 289 | 290 | - (NSString*)fix:(NSString *)string { 291 | NSMutableString* fixed = [[NSMutableString alloc] initWithCapacity:0]; 292 | int i, len = [string length]; 293 | for (i = 0; i < len; ++i) { 294 | unichar c = [string characterAtIndex:i]; 295 | if (![self isCaseSensitive:c]) { 296 | [fixed appendFormat:@"%C", [self smallCap:c]]; 297 | } 298 | else { 299 | [fixed appendFormat:@"%C", c]; 300 | } 301 | } 302 | [fixed autorelease]; 303 | return fixed; 304 | } 305 | 306 | @end 307 | -------------------------------------------------------------------------------- /CacheManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 7/1/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface CacheManager : NSObject { 11 | NSMutableDictionary* _weightCache; 12 | NSMutableDictionary* _phoneticCache; 13 | NSMutableDictionary* _recentBaseCache; 14 | } 15 | 16 | + (CacheManager *)sharedInstance; 17 | 18 | - (void)persist; 19 | 20 | // TODO - Rewrite the CacheManager with meaningful methods 21 | 22 | // Weight Cache (default for String) 23 | - (NSString*)stringForKey:(NSString*)aKey; 24 | - (void)removeStringForKey:(NSString*)aKey; 25 | - (void)setString:(NSString*)aString forKey:(NSString*)aKey; 26 | 27 | // Phonetic Cahce (default for Array) 28 | - (NSArray*)arrayForKey:(NSString*)aKey; 29 | - (void)setArray:(NSArray*)anArray forKey:(NSString*)aKey; 30 | 31 | // Base Cahce 32 | - (void)removeAllBase; 33 | - (NSArray*)baseForKey:(NSString*)aKey; 34 | - (void)setBase:(NSArray*)aBase forKey:(NSString*)aKey; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /CacheManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 7/1/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "CacheManager.h" 9 | 10 | static CacheManager* sharedInstance = nil; 11 | 12 | @implementation CacheManager 13 | 14 | + (CacheManager *)sharedInstance { 15 | if (sharedInstance == nil) { 16 | [[self alloc] init]; // assignment not done here, see allocWithZone 17 | } 18 | return sharedInstance; 19 | } 20 | 21 | + (id)allocWithZone:(NSZone *)zone { 22 | 23 | if (sharedInstance == nil) { 24 | sharedInstance = [super allocWithZone:zone]; 25 | return sharedInstance; // assignment and return on first allocation 26 | } 27 | return nil; //on subsequent allocation attempts return nil 28 | } 29 | 30 | - (id)copyWithZone:(NSZone *)zone { 31 | return self; 32 | } 33 | 34 | - (id)retain { 35 | return self; 36 | } 37 | 38 | - (oneway void)release { 39 | //do nothing 40 | } 41 | 42 | - (id)autorelease { 43 | return self; 44 | } 45 | 46 | - (NSUInteger)retainCount { 47 | return NSUIntegerMax; // This is sooo not zero 48 | } 49 | 50 | - (id)init { 51 | self = [super init]; 52 | if (self) { 53 | // Weight PLIST File 54 | NSString *path = [self getSharedFolder]; 55 | NSFileManager *fileManager = [NSFileManager defaultManager]; 56 | if ([fileManager fileExistsAtPath:path] == NO) { 57 | NSError* error = nil; 58 | [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; 59 | if (error) { 60 | @throw error; 61 | } 62 | } 63 | 64 | path = [path stringByAppendingPathComponent:@"weight.plist"]; 65 | 66 | if ([fileManager fileExistsAtPath:path]) { 67 | _weightCache = [[NSMutableDictionary alloc] initWithContentsOfFile:path]; 68 | } else { 69 | _weightCache = [[NSMutableDictionary alloc] initWithCapacity:0]; 70 | } 71 | _phoneticCache = [[NSMutableDictionary alloc] initWithCapacity:0]; 72 | _recentBaseCache = [[NSMutableDictionary alloc] initWithCapacity:0]; 73 | } 74 | return self; 75 | } 76 | 77 | - (void)dealloc { 78 | [self persist]; 79 | [_phoneticCache release]; 80 | [_recentBaseCache release]; 81 | [_weightCache release]; 82 | [super dealloc]; 83 | } 84 | 85 | - (void)persist { 86 | [_weightCache writeToFile:[[self getSharedFolder] stringByAppendingPathComponent:@"weight.plist"] atomically:YES]; 87 | } 88 | 89 | - (NSString*)getSharedFolder { 90 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); 91 | return [[[paths objectAtIndex:0] 92 | stringByAppendingPathComponent:@"OmicronLab"] 93 | stringByAppendingPathComponent:@"Avro Keyboard"]; 94 | } 95 | 96 | // Weight Cache 97 | - (NSString*)stringForKey:(NSString*)aKey { 98 | return [_weightCache objectForKey:aKey]; 99 | } 100 | 101 | - (void)removeStringForKey:(NSString*)aKey { 102 | [_weightCache removeObjectForKey:aKey]; 103 | } 104 | 105 | - (void)setString:(NSString*)aString forKey:(NSString*)aKey { 106 | [_weightCache setObject:aString forKey:aKey]; 107 | } 108 | 109 | // Phonetic Cache 110 | - (NSArray*)arrayForKey:(NSString*)aKey { 111 | return [_phoneticCache objectForKey:aKey]; 112 | } 113 | 114 | - (void)setArray:(NSArray*)anArray forKey:(NSString*)aKey { 115 | [_phoneticCache setObject:anArray forKey:aKey]; 116 | } 117 | 118 | // Base Cache 119 | - (void)removeAllBase { 120 | [_recentBaseCache removeAllObjects]; 121 | } 122 | 123 | - (NSArray*)baseForKey:(NSString*)aKey { 124 | return [_recentBaseCache objectForKey:aKey]; 125 | } 126 | 127 | - (void)setBase:(NSArray*)aBase forKey:(NSString*)aKey { 128 | [_recentBaseCache setObject:aBase forKey:aKey]; 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /Candidates.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/21/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @interface Candidates : IMKCandidates 12 | 13 | + (void)allocateSharedInstanceWithServer:(IMKServer *)server; 14 | + (void)deallocateSharedInstance; 15 | + (Candidates *)sharedInstance; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Candidates.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/21/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "Candidates.h" 9 | 10 | static Candidates *_sharedInstance = nil; 11 | 12 | @implementation Candidates 13 | 14 | + (void)allocateSharedInstanceWithServer:(IMKServer *)server { 15 | if (_sharedInstance == nil) { 16 | _sharedInstance = [[self alloc] initWithServer:server panelType:kIMKSingleColumnScrollingCandidatePanel]; 17 | [_sharedInstance setAttributes:[NSDictionary 18 | dictionaryWithObject:[NSNumber numberWithBool:YES] 19 | forKey:IMKCandidatesSendServerKeyEventFirst]]; 20 | 21 | [_sharedInstance setDismissesAutomatically:NO]; 22 | } 23 | } 24 | 25 | + (void)deallocateSharedInstance { 26 | [_sharedInstance release]; 27 | } 28 | 29 | + (Candidates *)sharedInstance; { 30 | return _sharedInstance; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Credits.rtfd/Pasted Graphic 1.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/Credits.rtfd/Pasted Graphic 1.tiff -------------------------------------------------------------------------------- /Credits.rtfd/Pasted Graphic 2.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/Credits.rtfd/Pasted Graphic 2.tiff -------------------------------------------------------------------------------- /Credits.rtfd/Pasted Graphic 4.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/Credits.rtfd/Pasted Graphic 4.tiff -------------------------------------------------------------------------------- /Credits.rtfd/Pasted Graphic 5.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/Credits.rtfd/Pasted Graphic 5.tiff -------------------------------------------------------------------------------- /Credits.rtfd/TXT.rtf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/Credits.rtfd/TXT.rtf -------------------------------------------------------------------------------- /Database.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/28/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface Database : NSObject { 11 | NSMutableDictionary* _db; 12 | NSMutableDictionary* _suffix; 13 | } 14 | 15 | + (Database *)sharedInstance; 16 | 17 | - (NSArray*)find:(NSString*)term; 18 | - (NSString*)banglaForSuffix:(NSString*)suffix; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Database.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/28/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "Database.h" 9 | #import "FMDatabase.h" 10 | #import "RegexParser.h" 11 | #import "RegexKitLite.h" 12 | 13 | static Database* sharedInstance = nil; 14 | 15 | @implementation Database 16 | 17 | + (Database *)sharedInstance { 18 | if (sharedInstance == nil) { 19 | [[self alloc] init]; // assignment not done here, see allocWithZone 20 | } 21 | return sharedInstance; 22 | } 23 | 24 | + (id)allocWithZone:(NSZone *)zone { 25 | if (sharedInstance == nil) { 26 | sharedInstance = [super allocWithZone:zone]; 27 | return sharedInstance; // assignment and return on first allocation 28 | } 29 | return nil; //on subsequent allocation attempts return nil 30 | } 31 | 32 | - (id)copyWithZone:(NSZone *)zone { 33 | return self; 34 | } 35 | 36 | - (id)retain { 37 | return self; 38 | } 39 | 40 | - (oneway void)release { 41 | //do nothing 42 | } 43 | 44 | - (id)autorelease { 45 | return self; 46 | } 47 | 48 | - (NSUInteger)retainCount { 49 | return NSUIntegerMax; // This is sooo not zero 50 | } 51 | 52 | - (id)init { 53 | self = [super init]; 54 | if (self) { 55 | _db = [[NSMutableDictionary alloc] initWithCapacity:0]; 56 | _suffix = [[NSMutableDictionary alloc] initWithCapacity:0]; 57 | 58 | NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init]; 59 | 60 | NSString* filePath = [[NSBundle mainBundle] pathForResource:@"database" ofType:@"db3"]; 61 | FMDatabase *sqliteDb = [FMDatabase databaseWithPath:filePath]; 62 | [sqliteDb open]; 63 | 64 | [self loadTableWithName:@"A" fromDatabase:sqliteDb]; 65 | [self loadTableWithName:@"AA" fromDatabase:sqliteDb]; 66 | [self loadTableWithName:@"B" fromDatabase:sqliteDb]; 67 | [self loadTableWithName:@"BH" fromDatabase:sqliteDb]; 68 | [self loadTableWithName:@"C" fromDatabase:sqliteDb]; 69 | [self loadTableWithName:@"CH" fromDatabase:sqliteDb]; 70 | [self loadTableWithName:@"D" fromDatabase:sqliteDb]; 71 | [self loadTableWithName:@"Dd" fromDatabase:sqliteDb]; 72 | [self loadTableWithName:@"Ddh" fromDatabase:sqliteDb]; 73 | [self loadTableWithName:@"Dh" fromDatabase:sqliteDb]; 74 | [self loadTableWithName:@"E" fromDatabase:sqliteDb]; 75 | [self loadTableWithName:@"G" fromDatabase:sqliteDb]; 76 | [self loadTableWithName:@"Gh" fromDatabase:sqliteDb]; 77 | [self loadTableWithName:@"H" fromDatabase:sqliteDb]; 78 | [self loadTableWithName:@"I" fromDatabase:sqliteDb]; 79 | [self loadTableWithName:@"II" fromDatabase:sqliteDb]; 80 | [self loadTableWithName:@"J" fromDatabase:sqliteDb]; 81 | [self loadTableWithName:@"JH" fromDatabase:sqliteDb]; 82 | [self loadTableWithName:@"K" fromDatabase:sqliteDb]; 83 | [self loadTableWithName:@"KH" fromDatabase:sqliteDb]; 84 | [self loadTableWithName:@"Khandatta" fromDatabase:sqliteDb]; 85 | [self loadTableWithName:@"L" fromDatabase:sqliteDb]; 86 | [self loadTableWithName:@"M" fromDatabase:sqliteDb]; 87 | [self loadTableWithName:@"N" fromDatabase:sqliteDb]; 88 | [self loadTableWithName:@"NGA" fromDatabase:sqliteDb]; 89 | [self loadTableWithName:@"NN" fromDatabase:sqliteDb]; 90 | [self loadTableWithName:@"NYA" fromDatabase:sqliteDb]; 91 | [self loadTableWithName:@"O" fromDatabase:sqliteDb]; 92 | [self loadTableWithName:@"OI" fromDatabase:sqliteDb]; 93 | [self loadTableWithName:@"OU" fromDatabase:sqliteDb]; 94 | [self loadTableWithName:@"P" fromDatabase:sqliteDb]; 95 | [self loadTableWithName:@"PH" fromDatabase:sqliteDb]; 96 | [self loadTableWithName:@"R" fromDatabase:sqliteDb]; 97 | [self loadTableWithName:@"RR" fromDatabase:sqliteDb]; 98 | [self loadTableWithName:@"RRH" fromDatabase:sqliteDb]; 99 | [self loadTableWithName:@"RRI" fromDatabase:sqliteDb]; 100 | [self loadTableWithName:@"S" fromDatabase:sqliteDb]; 101 | [self loadTableWithName:@"SH" fromDatabase:sqliteDb]; 102 | [self loadTableWithName:@"SS" fromDatabase:sqliteDb]; 103 | [self loadTableWithName:@"T" fromDatabase:sqliteDb]; 104 | [self loadTableWithName:@"TH" fromDatabase:sqliteDb]; 105 | [self loadTableWithName:@"TT" fromDatabase:sqliteDb]; 106 | [self loadTableWithName:@"TTH" fromDatabase:sqliteDb]; 107 | [self loadTableWithName:@"U" fromDatabase:sqliteDb]; 108 | [self loadTableWithName:@"UU" fromDatabase:sqliteDb]; 109 | [self loadTableWithName:@"Y" fromDatabase:sqliteDb]; 110 | [self loadTableWithName:@"Z" fromDatabase:sqliteDb]; 111 | 112 | [self loadSuffixTableFromDatabase:sqliteDb]; 113 | 114 | [sqliteDb close]; 115 | 116 | [loopPool release]; 117 | } 118 | return self; 119 | } 120 | 121 | - (void)dealloc { 122 | [_db release]; 123 | [_suffix release]; 124 | [super dealloc]; 125 | } 126 | 127 | - (void)loadTableWithName:(NSString*)name fromDatabase:(FMDatabase*)sqliteDb { 128 | NSMutableArray* items = [[NSMutableArray alloc] init]; 129 | 130 | FMResultSet *results = [sqliteDb executeQuery:[NSString stringWithFormat:@"SELECT * FROM %@", name]]; 131 | while([results next]) { 132 | [items addObject:[results stringForColumn:@"Words"]]; 133 | } 134 | 135 | /* 136 | NSLog(@"-----------------------------------------------------------------"); 137 | NSLog(@"%d items added to key %@", count, name); 138 | NSLog(@"-----------------------------------------------------------------"); 139 | */ 140 | 141 | [_db setObject:items forKey:[name lowercaseString]]; 142 | 143 | [results close]; 144 | [items release]; 145 | } 146 | 147 | - (void)loadSuffixTableFromDatabase:(FMDatabase*)sqliteDb { 148 | FMResultSet *results = [sqliteDb executeQuery:[NSString stringWithFormat:@"SELECT * FROM Suffix"]]; 149 | while([results next]) { 150 | [_suffix setObject:[results stringForColumn:@"Bangla"] forKey:[results stringForColumn:@"English"]]; 151 | } 152 | [results close]; 153 | } 154 | 155 | - (NSArray*)find:(NSString*)term { 156 | // Left Most Character 157 | unichar lmc = [[term lowercaseString] characterAtIndex:0]; 158 | NSString* regex = [NSString stringWithFormat:@"^%@$", [[RegexParser sharedInstance] parse:term]]; 159 | NSMutableArray* tableList = [[NSMutableArray alloc] initWithCapacity:0]; 160 | NSMutableSet* suggestions = [[NSMutableSet alloc] initWithCapacity:0]; 161 | 162 | switch (lmc) { 163 | case 'a': 164 | [tableList addObjectsFromArray: 165 | [NSArray arrayWithObjects:@"a", @"aa", @"e", @"oi", @"o", @"nya", @"y", nil]]; 166 | break; 167 | case 'b': 168 | [tableList addObjectsFromArray: 169 | [NSArray arrayWithObjects:@"b", @"bh", nil]]; 170 | break; 171 | case 'c': 172 | [tableList addObjectsFromArray: 173 | [NSArray arrayWithObjects:@"c", @"ch", @"k", nil]]; 174 | break; 175 | case 'd': 176 | [tableList addObjectsFromArray: 177 | [NSArray arrayWithObjects:@"d", @"dh", @"dd", @"ddh", nil]]; 178 | break; 179 | case 'e': 180 | [tableList addObjectsFromArray: 181 | [NSArray arrayWithObjects:@"i", @"ii", @"e", @"y", nil]]; 182 | break; 183 | case 'f': 184 | [tableList addObjectsFromArray: 185 | [NSArray arrayWithObjects:@"ph", nil]]; 186 | break; 187 | case 'g': 188 | [tableList addObjectsFromArray: 189 | [NSArray arrayWithObjects:@"g", @"gh", @"j", nil]]; 190 | break; 191 | case 'h': 192 | [tableList addObjectsFromArray: 193 | [NSArray arrayWithObjects:@"h", nil]]; 194 | break; 195 | case 'i': 196 | [tableList addObjectsFromArray: 197 | [NSArray arrayWithObjects:@"i", @"ii", @"y", nil]]; 198 | break; 199 | case 'j': 200 | [tableList addObjectsFromArray: 201 | [NSArray arrayWithObjects:@"j", @"jh", @"z", nil]]; 202 | break; 203 | case 'k': 204 | [tableList addObjectsFromArray: 205 | [NSArray arrayWithObjects:@"k", @"kh", nil]]; 206 | break; 207 | case 'l': 208 | [tableList addObjectsFromArray: 209 | [NSArray arrayWithObjects:@"l", nil]]; 210 | break; 211 | case 'm': 212 | [tableList addObjectsFromArray: 213 | [NSArray arrayWithObjects:@"h", @"m", nil]]; 214 | break; 215 | case 'n': 216 | [tableList addObjectsFromArray: 217 | [NSArray arrayWithObjects:@"n", @"nya", @"nga", @"nn", nil]]; 218 | break; 219 | case 'o': 220 | [tableList addObjectsFromArray: 221 | [NSArray arrayWithObjects:@"a", @"u", @"uu", @"oi", @"o", @"ou", @"y", nil]]; 222 | break; 223 | case 'p': 224 | [tableList addObjectsFromArray: 225 | [NSArray arrayWithObjects:@"p", @"ph", nil]]; 226 | break; 227 | case 'q': 228 | [tableList addObjectsFromArray: 229 | [NSArray arrayWithObjects:@"k", nil]]; 230 | break; 231 | case 'r': 232 | [tableList addObjectsFromArray: 233 | [NSArray arrayWithObjects:@"rri", @"h", @"r", @"rr", @"rrh", nil]]; 234 | break; 235 | case 's': 236 | [tableList addObjectsFromArray: 237 | [NSArray arrayWithObjects:@"s", @"sh", @"ss", nil]]; 238 | break; 239 | case 't': 240 | [tableList addObjectsFromArray: 241 | [NSArray arrayWithObjects:@"t", @"th", @"tt", @"tth", @"khandatta", nil]]; 242 | break; 243 | case 'u': 244 | [tableList addObjectsFromArray: 245 | [NSArray arrayWithObjects:@"u", @"uu", @"y", nil]]; 246 | break; 247 | case 'v': 248 | [tableList addObjectsFromArray: 249 | [NSArray arrayWithObjects:@"bh", nil]]; 250 | break; 251 | case 'w': 252 | [tableList addObjectsFromArray: 253 | [NSArray arrayWithObjects:@"o", nil]]; 254 | break; 255 | case 'x': 256 | [tableList addObjectsFromArray: 257 | [NSArray arrayWithObjects:@"e", @"k", nil]]; 258 | break; 259 | case 'y': 260 | [tableList addObjectsFromArray: 261 | [NSArray arrayWithObjects:@"i", @"y", nil]]; 262 | break; 263 | case 'z': 264 | [tableList addObjectsFromArray: 265 | [NSArray arrayWithObjects:@"h", @"j", @"jh", @"z", nil]]; 266 | break; 267 | default: 268 | break; 269 | } 270 | 271 | for (NSString* table in tableList) { 272 | NSArray* tableData = [_db objectForKey:table]; 273 | for (NSString* tmpString in tableData) { 274 | if ([tmpString isMatchedByRegex:regex]) { 275 | [suggestions addObject:tmpString]; 276 | } 277 | } 278 | } 279 | 280 | [tableList release]; 281 | [suggestions autorelease]; 282 | 283 | return [suggestions allObjects]; 284 | } 285 | 286 | - (NSString*)banglaForSuffix:(NSString*)suffix { 287 | return [_suffix objectForKey:suffix]; 288 | } 289 | 290 | @end 291 | -------------------------------------------------------------------------------- /English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /English.lproj/MainMenu.nib/designable.nib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1070 5 | 11E53 6 | 2182 7 | 1138.47 8 | 569.00 9 | 10 | com.apple.InterfaceBuilder.CocoaPlugin 11 | 2182 12 | 13 | 14 | NSMenu 15 | NSMenuItem 16 | NSCustomObject 17 | 18 | 19 | com.apple.InterfaceBuilder.CocoaPlugin 20 | 21 | 22 | PluginDependencyRecalculationVersion 23 | 24 | 25 | 26 | 27 | NSApplication 28 | 29 | 30 | FirstResponder 31 | 32 | 33 | NSApplication 34 | 35 | 36 | MainMenuAppDelegate 37 | 38 | 39 | Menu 40 | 41 | 42 | 43 | Preferences... 44 | 45 | 1048576 46 | 2147483647 47 | 48 | NSImage 49 | NSMenuCheckmark 50 | 51 | 52 | NSImage 53 | NSMenuMixedState 54 | 55 | 1 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | delegate 66 | 67 | 68 | 69 | 277 70 | 71 | 72 | 73 | _menu 74 | 75 | 76 | 77 | 276 78 | 79 | 80 | 81 | 82 | 83 | 0 84 | 85 | 86 | 87 | 88 | 89 | -2 90 | 91 | 92 | File's Owner 93 | 94 | 95 | -1 96 | 97 | 98 | First Responder 99 | 100 | 101 | 268 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 269 110 | 111 | 112 | 113 | 114 | -3 115 | 116 | 117 | Application 118 | 119 | 120 | 275 121 | 122 | 123 | 124 | 125 | 126 | 127 | com.apple.InterfaceBuilder.CocoaPlugin 128 | com.apple.InterfaceBuilder.CocoaPlugin 129 | com.apple.InterfaceBuilder.CocoaPlugin 130 | com.apple.InterfaceBuilder.CocoaPlugin 131 | com.apple.InterfaceBuilder.CocoaPlugin 132 | com.apple.InterfaceBuilder.CocoaPlugin 133 | 134 | 135 | 136 | 137 | 138 | 277 139 | 140 | 141 | 142 | 143 | MainMenuAppDelegate 144 | NSObject 145 | 146 | _menu 147 | NSMenu 148 | 149 | 150 | _menu 151 | 152 | _menu 153 | NSMenu 154 | 155 | 156 | 157 | IBProjectSource 158 | ./Classes/MainMenuAppDelegate.h 159 | 160 | 161 | 162 | 163 | 0 164 | IBCocoaFramework 165 | 166 | com.apple.InterfaceBuilder.CocoaPlugin.macosx 167 | 168 | 169 | YES 170 | 3 171 | 172 | {11, 11} 173 | {10, 3} 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /English.lproj/MainMenu.nib/keyedobjects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/English.lproj/MainMenu.nib/keyedobjects.nib -------------------------------------------------------------------------------- /English.lproj/preferences.nib/keyedobjects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/English.lproj/preferences.nib/keyedobjects.nib -------------------------------------------------------------------------------- /FMDatabase.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "sqlite3.h" 3 | #import "FMResultSet.h" 4 | #import "FMDatabasePool.h" 5 | 6 | 7 | #if ! __has_feature(objc_arc) 8 | #define FMDBAutorelease(__v) ([__v autorelease]); 9 | #define FMDBReturnAutoreleased FMDBAutorelease 10 | 11 | #define FMDBRetain(__v) ([__v retain]); 12 | #define FMDBReturnRetained FMDBRetain 13 | 14 | #define FMDBRelease(__v) ([__v release]); 15 | #else 16 | // -fobjc-arc 17 | #define FMDBAutorelease(__v) 18 | #define FMDBReturnAutoreleased(__v) (__v) 19 | 20 | #define FMDBRetain(__v) 21 | #define FMDBReturnRetained(__v) (__v) 22 | 23 | #define FMDBRelease(__v) 24 | #endif 25 | 26 | 27 | @interface FMDatabase : NSObject { 28 | 29 | sqlite3* _db; 30 | NSString* _databasePath; 31 | BOOL _logsErrors; 32 | BOOL _crashOnErrors; 33 | BOOL _traceExecution; 34 | BOOL _checkedOut; 35 | BOOL _shouldCacheStatements; 36 | BOOL _isExecutingStatement; 37 | BOOL _inTransaction; 38 | int _busyRetryTimeout; 39 | 40 | NSMutableDictionary *_cachedStatements; 41 | NSMutableSet *_openResultSets; 42 | NSMutableSet *_openFunctions; 43 | 44 | } 45 | 46 | 47 | @property (assign) BOOL traceExecution; 48 | @property (assign) BOOL checkedOut; 49 | @property (assign) int busyRetryTimeout; 50 | @property (assign) BOOL crashOnErrors; 51 | @property (assign) BOOL logsErrors; 52 | @property (retain) NSMutableDictionary *cachedStatements; 53 | 54 | 55 | + (id)databaseWithPath:(NSString*)inPath; 56 | - (id)initWithPath:(NSString*)inPath; 57 | 58 | - (BOOL)open; 59 | #if SQLITE_VERSION_NUMBER >= 3005000 60 | - (BOOL)openWithFlags:(int)flags; 61 | #endif 62 | - (BOOL)close; 63 | - (BOOL)goodConnection; 64 | - (void)clearCachedStatements; 65 | - (void)closeOpenResultSets; 66 | - (BOOL)hasOpenResultSets; 67 | 68 | // encryption methods. You need to have purchased the sqlite encryption extensions for these to work. 69 | - (BOOL)setKey:(NSString*)key; 70 | - (BOOL)rekey:(NSString*)key; 71 | 72 | - (NSString *)databasePath; 73 | 74 | - (NSString*)lastErrorMessage; 75 | 76 | - (int)lastErrorCode; 77 | - (BOOL)hadError; 78 | - (NSError*)lastError; 79 | 80 | - (sqlite_int64)lastInsertRowId; 81 | 82 | - (sqlite3*)sqliteHandle; 83 | 84 | - (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ...; 85 | - (BOOL)executeUpdate:(NSString*)sql, ...; 86 | - (BOOL)executeUpdateWithFormat:(NSString *)format, ...; 87 | - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments; 88 | - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments; 89 | 90 | - (FMResultSet *)executeQuery:(NSString*)sql, ...; 91 | - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...; 92 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments; 93 | - (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments; 94 | 95 | - (BOOL)rollback; 96 | - (BOOL)commit; 97 | - (BOOL)beginTransaction; 98 | - (BOOL)beginDeferredTransaction; 99 | - (BOOL)inTransaction; 100 | - (BOOL)shouldCacheStatements; 101 | - (void)setShouldCacheStatements:(BOOL)value; 102 | 103 | #if SQLITE_VERSION_NUMBER >= 3007000 104 | - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr; 105 | - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr; 106 | - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr; 107 | - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block; 108 | #endif 109 | 110 | + (BOOL)isSQLiteThreadSafe; 111 | + (NSString*)sqliteLibVersion; 112 | 113 | - (int)changes; 114 | 115 | - (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block; 116 | 117 | @end 118 | 119 | @interface FMStatement : NSObject { 120 | sqlite3_stmt *_statement; 121 | NSString *_query; 122 | long _useCount; 123 | } 124 | 125 | @property (assign) long useCount; 126 | @property (retain) NSString *query; 127 | @property (assign) sqlite3_stmt *statement; 128 | 129 | - (void)close; 130 | - (void)reset; 131 | 132 | @end 133 | 134 | -------------------------------------------------------------------------------- /FMDatabasePool.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "sqlite3.h" 11 | 12 | /* 13 | 14 | ***README OR SUFFER*** 15 | Before using FMDatabasePool, please consider using FMDatabaseQueue instead. 16 | 17 | If you really really really know what you're doing and FMDatabasePool is what 18 | you really really need (ie, you're using a read only database), OK you can use 19 | it. But just be careful not to deadlock! 20 | 21 | For an example on deadlocking, search for: 22 | ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD 23 | in the main.m file. 24 | 25 | */ 26 | 27 | 28 | 29 | @class FMDatabase; 30 | 31 | @interface FMDatabasePool : NSObject { 32 | NSString *_path; 33 | 34 | dispatch_queue_t _lockQueue; 35 | 36 | NSMutableArray *_databaseInPool; 37 | NSMutableArray *_databaseOutPool; 38 | 39 | __unsafe_unretained id _delegate; 40 | 41 | NSUInteger _maximumNumberOfDatabasesToCreate; 42 | } 43 | 44 | @property (retain) NSString *path; 45 | @property (assign) id delegate; 46 | @property (assign) NSUInteger maximumNumberOfDatabasesToCreate; 47 | 48 | + (id)databasePoolWithPath:(NSString*)aPath; 49 | - (id)initWithPath:(NSString*)aPath; 50 | 51 | - (NSUInteger)countOfCheckedInDatabases; 52 | - (NSUInteger)countOfCheckedOutDatabases; 53 | - (NSUInteger)countOfOpenDatabases; 54 | - (void)releaseAllDatabases; 55 | 56 | - (void)inDatabase:(void (^)(FMDatabase *db))block; 57 | 58 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 59 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 60 | 61 | #if SQLITE_VERSION_NUMBER >= 3007000 62 | // NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. 63 | // If you need to nest, use FMDatabase's startSavePointWithName:error: instead. 64 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; 65 | #endif 66 | 67 | @end 68 | 69 | 70 | @interface NSObject (FMDatabasePoolDelegate) 71 | 72 | - (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database; 73 | 74 | @end 75 | 76 | -------------------------------------------------------------------------------- /FMDatabasePool.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import "FMDatabasePool.h" 10 | #import "FMDatabase.h" 11 | 12 | @interface FMDatabasePool() 13 | 14 | - (void)pushDatabaseBackInPool:(FMDatabase*)db; 15 | - (FMDatabase*)db; 16 | 17 | @end 18 | 19 | 20 | @implementation FMDatabasePool 21 | @synthesize path=_path; 22 | @synthesize delegate=_delegate; 23 | @synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate; 24 | 25 | 26 | + (id)databasePoolWithPath:(NSString*)aPath { 27 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); 28 | } 29 | 30 | - (id)initWithPath:(NSString*)aPath { 31 | 32 | self = [super init]; 33 | 34 | if (self != nil) { 35 | _path = [aPath copy]; 36 | _lockQueue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 37 | _databaseInPool = FMDBReturnRetained([NSMutableArray array]); 38 | _databaseOutPool = FMDBReturnRetained([NSMutableArray array]); 39 | } 40 | 41 | return self; 42 | } 43 | 44 | - (void)dealloc { 45 | 46 | _delegate = 0x00; 47 | FMDBRelease(_path); 48 | FMDBRelease(_databaseInPool); 49 | FMDBRelease(_databaseOutPool); 50 | 51 | if (_lockQueue) { 52 | dispatch_release(_lockQueue); 53 | _lockQueue = 0x00; 54 | } 55 | #if ! __has_feature(objc_arc) 56 | [super dealloc]; 57 | #endif 58 | } 59 | 60 | 61 | - (void)executeLocked:(void (^)(void))aBlock { 62 | dispatch_sync(_lockQueue, aBlock); 63 | } 64 | 65 | - (void)pushDatabaseBackInPool:(FMDatabase*)db { 66 | 67 | if (!db) { // db can be null if we set an upper bound on the # of databases to create. 68 | return; 69 | } 70 | 71 | [self executeLocked:^() { 72 | 73 | if ([_databaseInPool containsObject:db]) { 74 | [[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise]; 75 | } 76 | 77 | [_databaseInPool addObject:db]; 78 | [_databaseOutPool removeObject:db]; 79 | 80 | }]; 81 | } 82 | 83 | - (FMDatabase*)db { 84 | 85 | __block FMDatabase *db; 86 | 87 | [self executeLocked:^() { 88 | db = [_databaseInPool lastObject]; 89 | 90 | if (db) { 91 | [_databaseOutPool addObject:db]; 92 | [_databaseInPool removeLastObject]; 93 | } 94 | else { 95 | 96 | if (_maximumNumberOfDatabasesToCreate) { 97 | NSUInteger currentCount = [_databaseOutPool count] + [_databaseInPool count]; 98 | 99 | if (currentCount >= _maximumNumberOfDatabasesToCreate) { 100 | NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount); 101 | return; 102 | } 103 | } 104 | 105 | db = [FMDatabase databaseWithPath:_path]; 106 | } 107 | 108 | //This ensures that the db is opened before returning 109 | if ([db open]) { 110 | if ([_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![_delegate databasePool:self shouldAddDatabaseToPool:db]) { 111 | [db close]; 112 | db = 0x00; 113 | } 114 | else { 115 | //It should not get added in the pool twice if lastObject was found 116 | if (![_databaseOutPool containsObject:db]) { 117 | [_databaseOutPool addObject:db]; 118 | } 119 | } 120 | } 121 | else { 122 | NSLog(@"Could not open up the database at path %@", _path); 123 | db = 0x00; 124 | } 125 | }]; 126 | 127 | return db; 128 | } 129 | 130 | - (NSUInteger)countOfCheckedInDatabases { 131 | 132 | __block NSUInteger count; 133 | 134 | [self executeLocked:^() { 135 | count = [_databaseInPool count]; 136 | }]; 137 | 138 | return count; 139 | } 140 | 141 | - (NSUInteger)countOfCheckedOutDatabases { 142 | 143 | __block NSUInteger count; 144 | 145 | [self executeLocked:^() { 146 | count = [_databaseOutPool count]; 147 | }]; 148 | 149 | return count; 150 | } 151 | 152 | - (NSUInteger)countOfOpenDatabases { 153 | __block NSUInteger count; 154 | 155 | [self executeLocked:^() { 156 | count = [_databaseOutPool count] + [_databaseInPool count]; 157 | }]; 158 | 159 | return count; 160 | } 161 | 162 | - (void)releaseAllDatabases { 163 | [self executeLocked:^() { 164 | [_databaseOutPool removeAllObjects]; 165 | [_databaseInPool removeAllObjects]; 166 | }]; 167 | } 168 | 169 | - (void)inDatabase:(void (^)(FMDatabase *db))block { 170 | 171 | FMDatabase *db = [self db]; 172 | 173 | block(db); 174 | 175 | [self pushDatabaseBackInPool:db]; 176 | } 177 | 178 | - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 179 | 180 | BOOL shouldRollback = NO; 181 | 182 | FMDatabase *db = [self db]; 183 | 184 | if (useDeferred) { 185 | [db beginDeferredTransaction]; 186 | } 187 | else { 188 | [db beginTransaction]; 189 | } 190 | 191 | 192 | block(db, &shouldRollback); 193 | 194 | if (shouldRollback) { 195 | [db rollback]; 196 | } 197 | else { 198 | [db commit]; 199 | } 200 | 201 | [self pushDatabaseBackInPool:db]; 202 | } 203 | 204 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 205 | [self beginTransaction:YES withBlock:block]; 206 | } 207 | 208 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 209 | [self beginTransaction:NO withBlock:block]; 210 | } 211 | #if SQLITE_VERSION_NUMBER >= 3007000 212 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { 213 | 214 | static unsigned long savePointIdx = 0; 215 | 216 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 217 | 218 | BOOL shouldRollback = NO; 219 | 220 | FMDatabase *db = [self db]; 221 | 222 | NSError *err = 0x00; 223 | 224 | if (![db startSavePointWithName:name error:&err]) { 225 | [self pushDatabaseBackInPool:db]; 226 | return err; 227 | } 228 | 229 | block(db, &shouldRollback); 230 | 231 | if (shouldRollback) { 232 | [db rollbackToSavePointWithName:name error:&err]; 233 | } 234 | else { 235 | [db releaseSavePointWithName:name error:&err]; 236 | } 237 | 238 | [self pushDatabaseBackInPool:db]; 239 | 240 | return err; 241 | } 242 | #endif 243 | 244 | @end 245 | -------------------------------------------------------------------------------- /FMResultSet.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "sqlite3.h" 3 | 4 | #ifndef __has_feature // Optional. 5 | #define __has_feature(x) 0 // Compatibility with non-clang compilers. 6 | #endif 7 | 8 | #ifndef NS_RETURNS_NOT_RETAINED 9 | #if __has_feature(attribute_ns_returns_not_retained) 10 | #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) 11 | #else 12 | #define NS_RETURNS_NOT_RETAINED 13 | #endif 14 | #endif 15 | 16 | @class FMDatabase; 17 | @class FMStatement; 18 | 19 | @interface FMResultSet : NSObject { 20 | FMDatabase *_parentDB; 21 | FMStatement *_statement; 22 | 23 | NSString *_query; 24 | NSMutableDictionary *_columnNameToIndexMap; 25 | BOOL _columnNamesSetup; 26 | } 27 | 28 | @property (retain) NSString *query; 29 | @property (retain) NSMutableDictionary *columnNameToIndexMap; 30 | @property (retain) FMStatement *statement; 31 | 32 | + (id)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB; 33 | 34 | - (void)close; 35 | 36 | - (void)setParentDB:(FMDatabase *)newDb; 37 | 38 | - (BOOL)next; 39 | - (BOOL)hasAnotherRow; 40 | 41 | - (int)columnCount; 42 | 43 | - (int)columnIndexForName:(NSString*)columnName; 44 | - (NSString*)columnNameForIndex:(int)columnIdx; 45 | 46 | - (int)intForColumn:(NSString*)columnName; 47 | - (int)intForColumnIndex:(int)columnIdx; 48 | 49 | - (long)longForColumn:(NSString*)columnName; 50 | - (long)longForColumnIndex:(int)columnIdx; 51 | 52 | - (long long int)longLongIntForColumn:(NSString*)columnName; 53 | - (long long int)longLongIntForColumnIndex:(int)columnIdx; 54 | 55 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName; 56 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx; 57 | 58 | - (BOOL)boolForColumn:(NSString*)columnName; 59 | - (BOOL)boolForColumnIndex:(int)columnIdx; 60 | 61 | - (double)doubleForColumn:(NSString*)columnName; 62 | - (double)doubleForColumnIndex:(int)columnIdx; 63 | 64 | - (NSString*)stringForColumn:(NSString*)columnName; 65 | - (NSString*)stringForColumnIndex:(int)columnIdx; 66 | 67 | - (NSDate*)dateForColumn:(NSString*)columnName; 68 | - (NSDate*)dateForColumnIndex:(int)columnIdx; 69 | 70 | - (NSData*)dataForColumn:(NSString*)columnName; 71 | - (NSData*)dataForColumnIndex:(int)columnIdx; 72 | 73 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx; 74 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName; 75 | 76 | // returns one of NSNumber, NSString, NSData, or NSNull 77 | - (id)objectForColumnName:(NSString*)columnName; 78 | - (id)objectForColumnIndex:(int)columnIdx; 79 | 80 | /* 81 | If you are going to use this data after you iterate over the next row, or after you close the 82 | result set, make sure to make a copy of the data first (or just use dataForColumn:/dataForColumnIndex:) 83 | If you don't, you're going to be in a world of hurt when you try and use the data. 84 | */ 85 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName NS_RETURNS_NOT_RETAINED; 86 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED; 87 | 88 | - (BOOL)columnIndexIsNull:(int)columnIdx; 89 | - (BOOL)columnIsNull:(NSString*)columnName; 90 | 91 | 92 | /* Returns a dictionary of the row results mapped to case sensitive keys of the column names. */ 93 | - (NSDictionary*)resultDictionary; 94 | 95 | /* Please use resultDictionary instead. Also, beware that resultDictionary is case sensitive! */ 96 | - (NSDictionary*)resultDict __attribute__ ((deprecated)); 97 | 98 | - (void)kvcMagic:(id)object; 99 | 100 | 101 | @end 102 | 103 | -------------------------------------------------------------------------------- /FMResultSet.m: -------------------------------------------------------------------------------- 1 | #import "FMResultSet.h" 2 | #import "FMDatabase.h" 3 | #import "unistd.h" 4 | 5 | @interface FMDatabase () 6 | - (void)resultSetDidClose:(FMResultSet *)resultSet; 7 | @end 8 | 9 | 10 | @interface FMResultSet (Private) 11 | - (NSMutableDictionary *)columnNameToIndexMap; 12 | - (void)setColumnNameToIndexMap:(NSMutableDictionary *)value; 13 | @end 14 | 15 | @implementation FMResultSet 16 | @synthesize query=_query; 17 | @synthesize columnNameToIndexMap=_columnNameToIndexMap; 18 | @synthesize statement=_statement; 19 | 20 | + (id)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB { 21 | 22 | FMResultSet *rs = [[FMResultSet alloc] init]; 23 | 24 | [rs setStatement:statement]; 25 | [rs setParentDB:aDB]; 26 | 27 | return FMDBReturnAutoreleased(rs); 28 | } 29 | 30 | - (void)finalize { 31 | [self close]; 32 | [super finalize]; 33 | } 34 | 35 | - (void)dealloc { 36 | [self close]; 37 | 38 | FMDBRelease(_query); 39 | _query = nil; 40 | 41 | FMDBRelease(_columnNameToIndexMap); 42 | _columnNameToIndexMap = nil; 43 | 44 | #if ! __has_feature(objc_arc) 45 | [super dealloc]; 46 | #endif 47 | } 48 | 49 | - (void)close { 50 | [_statement reset]; 51 | FMDBRelease(_statement); 52 | _statement = nil; 53 | 54 | // we don't need this anymore... (i think) 55 | //[_parentDB setInUse:NO]; 56 | [_parentDB resultSetDidClose:self]; 57 | _parentDB = nil; 58 | } 59 | 60 | - (int)columnCount { 61 | return sqlite3_column_count([_statement statement]); 62 | } 63 | 64 | - (void)setupColumnNames { 65 | 66 | if (!_columnNameToIndexMap) { 67 | [self setColumnNameToIndexMap:[NSMutableDictionary dictionary]]; 68 | } 69 | 70 | int columnCount = sqlite3_column_count([_statement statement]); 71 | 72 | int columnIdx = 0; 73 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 74 | [_columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx] 75 | forKey:[[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)] lowercaseString]]; 76 | } 77 | _columnNamesSetup = YES; 78 | } 79 | 80 | - (void)kvcMagic:(id)object { 81 | 82 | int columnCount = sqlite3_column_count([_statement statement]); 83 | 84 | int columnIdx = 0; 85 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 86 | 87 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 88 | 89 | // check for a null row 90 | if (c) { 91 | NSString *s = [NSString stringWithUTF8String:c]; 92 | 93 | [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]]; 94 | } 95 | } 96 | } 97 | 98 | #pragma clang diagnostic push 99 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 100 | 101 | - (NSDictionary*)resultDict { 102 | 103 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 104 | 105 | if (num_cols > 0) { 106 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 107 | 108 | if (!_columnNamesSetup) { 109 | [self setupColumnNames]; 110 | } 111 | 112 | NSEnumerator *columnNames = [_columnNameToIndexMap keyEnumerator]; 113 | NSString *columnName = nil; 114 | while ((columnName = [columnNames nextObject])) { 115 | id objectValue = [self objectForColumnName:columnName]; 116 | [dict setObject:objectValue forKey:columnName]; 117 | } 118 | 119 | return FMDBReturnAutoreleased([dict copy]); 120 | } 121 | else { 122 | NSLog(@"Warning: There seem to be no columns in this set."); 123 | } 124 | 125 | return nil; 126 | } 127 | 128 | #pragma clang diagnostic pop 129 | 130 | - (NSDictionary*)resultDictionary { 131 | 132 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 133 | 134 | if (num_cols > 0) { 135 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 136 | 137 | int columnCount = sqlite3_column_count([_statement statement]); 138 | 139 | int columnIdx = 0; 140 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 141 | 142 | NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]; 143 | id objectValue = [self objectForColumnIndex:columnIdx]; 144 | [dict setObject:objectValue forKey:columnName]; 145 | } 146 | 147 | return dict; 148 | } 149 | else { 150 | NSLog(@"Warning: There seem to be no columns in this set."); 151 | } 152 | 153 | return nil; 154 | } 155 | 156 | 157 | 158 | 159 | 160 | - (BOOL)next { 161 | 162 | int rc; 163 | BOOL retry; 164 | int numberOfRetries = 0; 165 | do { 166 | retry = NO; 167 | 168 | rc = sqlite3_step([_statement statement]); 169 | 170 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 171 | // this will happen if the db is locked, like if we are doing an update or insert. 172 | // in that case, retry the step... and maybe wait just 10 milliseconds. 173 | retry = YES; 174 | if (SQLITE_LOCKED == rc) { 175 | rc = sqlite3_reset([_statement statement]); 176 | if (rc != SQLITE_LOCKED) { 177 | NSLog(@"Unexpected result from sqlite3_reset (%d) rs", rc); 178 | } 179 | } 180 | usleep(20); 181 | 182 | if ([_parentDB busyRetryTimeout] && (numberOfRetries++ > [_parentDB busyRetryTimeout])) { 183 | 184 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]); 185 | NSLog(@"Database busy"); 186 | break; 187 | } 188 | } 189 | else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { 190 | // all is well, let's return. 191 | } 192 | else if (SQLITE_ERROR == rc) { 193 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 194 | break; 195 | } 196 | else if (SQLITE_MISUSE == rc) { 197 | // uh oh. 198 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 199 | break; 200 | } 201 | else { 202 | // wtf? 203 | NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 204 | break; 205 | } 206 | 207 | } while (retry); 208 | 209 | 210 | if (rc != SQLITE_ROW) { 211 | [self close]; 212 | } 213 | 214 | return (rc == SQLITE_ROW); 215 | } 216 | 217 | - (BOOL)hasAnotherRow { 218 | return sqlite3_errcode([_parentDB sqliteHandle]) == SQLITE_ROW; 219 | } 220 | 221 | - (int)columnIndexForName:(NSString*)columnName { 222 | 223 | if (!_columnNamesSetup) { 224 | [self setupColumnNames]; 225 | } 226 | 227 | columnName = [columnName lowercaseString]; 228 | 229 | NSNumber *n = [_columnNameToIndexMap objectForKey:columnName]; 230 | 231 | if (n) { 232 | return [n intValue]; 233 | } 234 | 235 | NSLog(@"Warning: I could not find the column named '%@'.", columnName); 236 | 237 | return -1; 238 | } 239 | 240 | 241 | 242 | - (int)intForColumn:(NSString*)columnName { 243 | return [self intForColumnIndex:[self columnIndexForName:columnName]]; 244 | } 245 | 246 | - (int)intForColumnIndex:(int)columnIdx { 247 | return sqlite3_column_int([_statement statement], columnIdx); 248 | } 249 | 250 | - (long)longForColumn:(NSString*)columnName { 251 | return [self longForColumnIndex:[self columnIndexForName:columnName]]; 252 | } 253 | 254 | - (long)longForColumnIndex:(int)columnIdx { 255 | return (long)sqlite3_column_int64([_statement statement], columnIdx); 256 | } 257 | 258 | - (long long int)longLongIntForColumn:(NSString*)columnName { 259 | return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]]; 260 | } 261 | 262 | - (long long int)longLongIntForColumnIndex:(int)columnIdx { 263 | return sqlite3_column_int64([_statement statement], columnIdx); 264 | } 265 | 266 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName { 267 | return [self unsignedLongLongIntForColumnIndex:[self columnIndexForName:columnName]]; 268 | } 269 | 270 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx { 271 | return (unsigned long long int)[self longLongIntForColumnIndex:columnIdx]; 272 | } 273 | 274 | - (BOOL)boolForColumn:(NSString*)columnName { 275 | return [self boolForColumnIndex:[self columnIndexForName:columnName]]; 276 | } 277 | 278 | - (BOOL)boolForColumnIndex:(int)columnIdx { 279 | return ([self intForColumnIndex:columnIdx] != 0); 280 | } 281 | 282 | - (double)doubleForColumn:(NSString*)columnName { 283 | return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; 284 | } 285 | 286 | - (double)doubleForColumnIndex:(int)columnIdx { 287 | return sqlite3_column_double([_statement statement], columnIdx); 288 | } 289 | 290 | - (NSString*)stringForColumnIndex:(int)columnIdx { 291 | 292 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 293 | return nil; 294 | } 295 | 296 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 297 | 298 | if (!c) { 299 | // null row. 300 | return nil; 301 | } 302 | 303 | return [NSString stringWithUTF8String:c]; 304 | } 305 | 306 | - (NSString*)stringForColumn:(NSString*)columnName { 307 | return [self stringForColumnIndex:[self columnIndexForName:columnName]]; 308 | } 309 | 310 | - (NSDate*)dateForColumn:(NSString*)columnName { 311 | return [self dateForColumnIndex:[self columnIndexForName:columnName]]; 312 | } 313 | 314 | - (NSDate*)dateForColumnIndex:(int)columnIdx { 315 | 316 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 317 | return nil; 318 | } 319 | 320 | return [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]]; 321 | } 322 | 323 | 324 | - (NSData*)dataForColumn:(NSString*)columnName { 325 | return [self dataForColumnIndex:[self columnIndexForName:columnName]]; 326 | } 327 | 328 | - (NSData*)dataForColumnIndex:(int)columnIdx { 329 | 330 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 331 | return nil; 332 | } 333 | 334 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 335 | 336 | NSMutableData *data = [NSMutableData dataWithLength:(NSUInteger)dataSize]; 337 | 338 | memcpy([data mutableBytes], sqlite3_column_blob([_statement statement], columnIdx), dataSize); 339 | 340 | return data; 341 | } 342 | 343 | 344 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName { 345 | return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]]; 346 | } 347 | 348 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx { 349 | 350 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 351 | return nil; 352 | } 353 | 354 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 355 | 356 | NSData *data = [NSData dataWithBytesNoCopy:(void *)sqlite3_column_blob([_statement statement], columnIdx) length:(NSUInteger)dataSize freeWhenDone:NO]; 357 | 358 | return data; 359 | } 360 | 361 | 362 | - (BOOL)columnIndexIsNull:(int)columnIdx { 363 | return sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL; 364 | } 365 | 366 | - (BOOL)columnIsNull:(NSString*)columnName { 367 | return [self columnIndexIsNull:[self columnIndexForName:columnName]]; 368 | } 369 | 370 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx { 371 | 372 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 373 | return nil; 374 | } 375 | 376 | return sqlite3_column_text([_statement statement], columnIdx); 377 | } 378 | 379 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName { 380 | return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]]; 381 | } 382 | 383 | - (id)objectForColumnIndex:(int)columnIdx { 384 | int columnType = sqlite3_column_type([_statement statement], columnIdx); 385 | 386 | id returnValue = nil; 387 | 388 | if (columnType == SQLITE_INTEGER) { 389 | returnValue = [NSNumber numberWithLongLong:[self longLongIntForColumnIndex:columnIdx]]; 390 | } 391 | else if (columnType == SQLITE_FLOAT) { 392 | returnValue = [NSNumber numberWithDouble:[self doubleForColumnIndex:columnIdx]]; 393 | } 394 | else if (columnType == SQLITE_BLOB) { 395 | returnValue = [self dataForColumnIndex:columnIdx]; 396 | } 397 | else { 398 | //default to a string for everything else 399 | returnValue = [self stringForColumnIndex:columnIdx]; 400 | } 401 | 402 | if (returnValue == nil) { 403 | returnValue = [NSNull null]; 404 | } 405 | 406 | return returnValue; 407 | } 408 | 409 | - (id)objectForColumnName:(NSString*)columnName { 410 | return [self objectForColumnIndex:[self columnIndexForName:columnName]]; 411 | } 412 | 413 | // returns autoreleased NSString containing the name of the column in the result set 414 | - (NSString*)columnNameForIndex:(int)columnIdx { 415 | return [NSString stringWithUTF8String: sqlite3_column_name([_statement statement], columnIdx)]; 416 | } 417 | 418 | - (void)setParentDB:(FMDatabase *)newDb { 419 | _parentDB = newDb; 420 | } 421 | 422 | 423 | @end 424 | -------------------------------------------------------------------------------- /Icons/AutoCorrect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/Icons/AutoCorrect.png -------------------------------------------------------------------------------- /Icons/Credits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/Icons/Credits.png -------------------------------------------------------------------------------- /Icons/General.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/Icons/General.png -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | Avro Keyboard 9 | CFBundleIconFile 10 | avro.icns 11 | CFBundleIdentifier 12 | com.omicronlab.inputmethod.AvroKeyboard 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | «PROJECTNAME» 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | InputMethodConnectionName 24 | Avro_Keyboard_Connection 25 | InputMethodServerControllerClass 26 | AvroKeyboardController 27 | LSBackgroundOnly 28 | 1 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | tsInputMethodCharacterRepertoireKey 34 | 35 | Latn 36 | 37 | tsInputMethodIconFileKey 38 | avro.icns 39 | 40 | 41 | -------------------------------------------------------------------------------- /MainMenuAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/24/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface MainMenuAppDelegate : NSObject { 11 | IBOutlet NSMenu* _menu; 12 | } 13 | 14 | -(NSMenu*)menu; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /MainMenuAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/24/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "MainMenuAppDelegate.h" 9 | #import "AutoCorrect.h" 10 | #import "CacheManager.h" 11 | #import "Database.h" 12 | #import "RegexParser.h" 13 | 14 | @implementation MainMenuAppDelegate 15 | 16 | //this method is added so that our controllers can access the shared NSMenu. 17 | -(NSMenu*)menu { 18 | return _menu; 19 | } 20 | 21 | //add an awakeFromNib item so that we can set the action method. Note that any menuItems without an action will be disabled when 22 | //displayed in the Text Input Menu. 23 | -(void)awakeFromNib { 24 | NSMenuItem* preferences = [_menu itemWithTag:1]; 25 | 26 | if (preferences) { 27 | [preferences setAction:@selector(showPreferences:)]; 28 | } 29 | 30 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeDictionary"]) { 31 | NSLog(@"Loading Dictionary..."); 32 | [Database sharedInstance]; 33 | [RegexParser sharedInstance]; 34 | [CacheManager sharedInstance]; 35 | } 36 | [AutoCorrect sharedInstance]; 37 | } 38 | 39 | - (void)applicationWillTerminate:(NSNotification *)notification { 40 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeDictionary"]) { 41 | [[CacheManager sharedInstance] persist]; 42 | } 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /NSString+Levenshtein.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Levenshtein.h 3 | // Levenshtein 4 | // 5 | // Created by Stefano Pigozzi on 8/20/09. 6 | // Copyright 2009 Stefano Pigozzi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSString (Levenshtein) 13 | 14 | -(int) computeLevenshteinDistanceWithString:(NSString *) string; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /NSString+Levenshtein.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Levenshtein.m 3 | // Levenshtein 4 | // 5 | // Created by Stefano Pigozzi on 8/20/09. 6 | // Copyright 2009 Stefano Pigozzi. All rights reserved. 7 | // 8 | 9 | #import "NSString+Levenshtein.h" 10 | #include 11 | 12 | @implementation NSString (Levenshtein) 13 | 14 | /// minimum between three values 15 | int minimum(int a,int b,int c) 16 | { 17 | int min=a; 18 | if(b 9 | 10 | @interface PreferencesController : NSWindowController { 11 | IBOutlet NSView* _aboutView; 12 | IBOutlet NSView* _autoCorrectView; 13 | IBOutlet NSView* _generalView; 14 | 15 | IBOutlet NSTextView* _aboutContent; 16 | 17 | int _currentViewTag; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /PreferencesController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/25/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "PreferencesController.h" 9 | 10 | @implementation PreferencesController 11 | 12 | - (NSRect)newFrameForNewContentView:(NSView*)view { 13 | NSWindow* window = [self window]; 14 | NSRect newFrameRect = [window frameRectForContentRect:[view frame]]; 15 | NSRect oldFrameRect = [window frame]; 16 | NSSize newSize = newFrameRect.size; 17 | NSSize oldSize = oldFrameRect.size; 18 | 19 | NSRect frame = [window frame]; 20 | frame.size = newSize; 21 | frame.origin.y -= (newSize.height - oldSize.height); 22 | 23 | return frame; 24 | } 25 | 26 | - (NSView*)viewForTag:(int)tag { 27 | NSView* view = nil; 28 | switch (tag) { 29 | case 0: 30 | view = _generalView; 31 | break; 32 | case 1: 33 | view = _autoCorrectView; 34 | break; 35 | case 2: default: 36 | view = _aboutView; 37 | break; 38 | } 39 | return view; 40 | } 41 | 42 | - (BOOL)validateToolbarItem:(NSToolbarItem *)item { 43 | if ([item tag] == _currentViewTag) { 44 | return NO; 45 | } 46 | return YES; 47 | } 48 | 49 | - (void)awakeFromNib { 50 | [[self window] setContentSize:[_generalView frame].size]; 51 | [[[self window] contentView] addSubview:_generalView]; 52 | [[[self window] contentView] setWantsLayer:YES]; 53 | 54 | // Load Credits 55 | [_aboutContent readRTFDFromFile:[[NSBundle mainBundle] pathForResource:@"Credits" ofType:@"rtfd"]]; 56 | [_aboutContent scrollToBeginningOfDocument:_aboutContent]; 57 | } 58 | 59 | - (IBAction)switchView:(id)sender { 60 | int tag = [sender tag]; 61 | NSView* view = [self viewForTag:tag]; 62 | NSView* previousView = [self viewForTag:_currentViewTag]; 63 | _currentViewTag = tag; 64 | NSRect newFrame = [self newFrameForNewContentView:view]; 65 | 66 | [NSAnimationContext beginGrouping]; 67 | 68 | if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) { 69 | [[NSAnimationContext currentContext] setDuration:1.0]; 70 | } 71 | 72 | [[[[self window] contentView] animator] replaceSubview:previousView with:view]; 73 | [[[self window] animator] setFrame:newFrame display:YES]; 74 | 75 | [NSAnimationContext endGrouping]; 76 | } 77 | @end 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/README.md -------------------------------------------------------------------------------- /RegexKitLite.h: -------------------------------------------------------------------------------- 1 | // 2 | // RegexKitLite.h 3 | // http://regexkit.sourceforge.net/ 4 | // Licensed under the terms of the BSD License, as specified below. 5 | // 6 | 7 | /* 8 | Copyright (c) 2008-2010, John Engelhart 9 | 10 | All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | * Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | 18 | * Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | * Neither the name of the Zang Industries nor the names of its 23 | contributors may be used to endorse or promote products derived from 24 | this software without specific prior written permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 32 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 33 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 34 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 35 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | 39 | #ifdef __OBJC__ 40 | #import 41 | #import 42 | #import 43 | #import 44 | #import 45 | #endif // __OBJC__ 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #ifdef __cplusplus 54 | extern "C" { 55 | #endif 56 | 57 | #ifndef REGEXKITLITE_VERSION_DEFINED 58 | #define REGEXKITLITE_VERSION_DEFINED 59 | 60 | #define _RKL__STRINGIFY(b) #b 61 | #define _RKL_STRINGIFY(a) _RKL__STRINGIFY(a) 62 | #define _RKL_JOIN_VERSION(a,b) _RKL_STRINGIFY(a##.##b) 63 | #define _RKL_VERSION_STRING(a,b) _RKL_JOIN_VERSION(a,b) 64 | 65 | #define REGEXKITLITE_VERSION_MAJOR 4 66 | #define REGEXKITLITE_VERSION_MINOR 0 67 | 68 | #define REGEXKITLITE_VERSION_CSTRING _RKL_VERSION_STRING(REGEXKITLITE_VERSION_MAJOR, REGEXKITLITE_VERSION_MINOR) 69 | #define REGEXKITLITE_VERSION_NSSTRING @REGEXKITLITE_VERSION_CSTRING 70 | 71 | #endif // REGEXKITLITE_VERSION_DEFINED 72 | 73 | #if !defined(RKL_BLOCKS) && defined(NS_BLOCKS_AVAILABLE) && (NS_BLOCKS_AVAILABLE == 1) 74 | #define RKL_BLOCKS 1 75 | #endif 76 | 77 | #if defined(RKL_BLOCKS) && (RKL_BLOCKS == 1) 78 | #define _RKL_BLOCKS_ENABLED 1 79 | #endif // defined(RKL_BLOCKS) && (RKL_BLOCKS == 1) 80 | 81 | #if defined(_RKL_BLOCKS_ENABLED) && !defined(__BLOCKS__) 82 | #warning RegexKitLite support for Blocks is enabled, but __BLOCKS__ is not defined. This compiler may not support Blocks, in which case the behavior is undefined. This will probably cause numerous compiler errors. 83 | #endif // defined(_RKL_BLOCKS_ENABLED) && !defined(__BLOCKS__) 84 | 85 | // For Mac OS X < 10.5. 86 | #ifndef NSINTEGER_DEFINED 87 | #define NSINTEGER_DEFINED 88 | #if defined(__LP64__) || defined(NS_BUILD_32_LIKE_64) 89 | typedef long NSInteger; 90 | typedef unsigned long NSUInteger; 91 | #define NSIntegerMin LONG_MIN 92 | #define NSIntegerMax LONG_MAX 93 | #define NSUIntegerMax ULONG_MAX 94 | #else // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64) 95 | typedef int NSInteger; 96 | typedef unsigned int NSUInteger; 97 | #define NSIntegerMin INT_MIN 98 | #define NSIntegerMax INT_MAX 99 | #define NSUIntegerMax UINT_MAX 100 | #endif // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64) 101 | #endif // NSINTEGER_DEFINED 102 | 103 | #ifndef RKLREGEXOPTIONS_DEFINED 104 | #define RKLREGEXOPTIONS_DEFINED 105 | 106 | // These must be identical to their ICU regex counterparts. See http://www.icu-project.org/userguide/regexp.html 107 | enum { 108 | RKLNoOptions = 0, 109 | RKLCaseless = 2, 110 | RKLComments = 4, 111 | RKLDotAll = 32, 112 | RKLMultiline = 8, 113 | RKLUnicodeWordBoundaries = 256 114 | }; 115 | typedef uint32_t RKLRegexOptions; // This must be identical to the ICU 'flags' argument type. 116 | 117 | #endif // RKLREGEXOPTIONS_DEFINED 118 | 119 | #ifndef RKLREGEXENUMERATIONOPTIONS_DEFINED 120 | #define RKLREGEXENUMERATIONOPTIONS_DEFINED 121 | 122 | enum { 123 | RKLRegexEnumerationNoOptions = 0UL, 124 | RKLRegexEnumerationCapturedStringsNotRequired = 1UL << 9, 125 | RKLRegexEnumerationReleaseStringReturnedByReplacementBlock = 1UL << 10, 126 | RKLRegexEnumerationFastCapturedStringsXXX = 1UL << 11, 127 | }; 128 | typedef NSUInteger RKLRegexEnumerationOptions; 129 | 130 | #endif // RKLREGEXENUMERATIONOPTIONS_DEFINED 131 | 132 | #ifndef _REGEXKITLITE_H_ 133 | #define _REGEXKITLITE_H_ 134 | 135 | #if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__APPLE_CC__) && (__APPLE_CC__ >= 5465) 136 | #define RKL_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) 137 | #else 138 | #define RKL_DEPRECATED_ATTRIBUTE 139 | #endif 140 | 141 | #if defined(NS_REQUIRES_NIL_TERMINATION) 142 | #define RKL_REQUIRES_NIL_TERMINATION NS_REQUIRES_NIL_TERMINATION 143 | #else // defined(NS_REQUIRES_NIL_TERMINATION) 144 | #define RKL_REQUIRES_NIL_TERMINATION 145 | #endif // defined(NS_REQUIRES_NIL_TERMINATION) 146 | 147 | // This requires a few levels of rewriting to get the desired results. 148 | #define _RKL_CONCAT_2(c,d) c ## d 149 | #define _RKL_CONCAT(a,b) _RKL_CONCAT_2(a,b) 150 | 151 | #ifdef RKL_PREPEND_TO_METHODS 152 | #define RKL_METHOD_PREPEND(x) _RKL_CONCAT(RKL_PREPEND_TO_METHODS, x) 153 | #else // RKL_PREPEND_TO_METHODS 154 | #define RKL_METHOD_PREPEND(x) x 155 | #endif // RKL_PREPEND_TO_METHODS 156 | 157 | // If it looks like low memory notifications might be available, add code to register and respond to them. 158 | // This is (should be) harmless if it turns out that this isn't the case, since the notification that we register for, 159 | // UIApplicationDidReceiveMemoryWarningNotification, is dynamically looked up via dlsym(). 160 | #if ((defined(TARGET_OS_EMBEDDED) && (TARGET_OS_EMBEDDED != 0)) || (defined(TARGET_OS_IPHONE) && (TARGET_OS_IPHONE != 0))) && (!defined(RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS) || (RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS != 0)) 161 | #define RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS 1 162 | #endif 163 | 164 | #ifdef __OBJC__ 165 | 166 | // NSException exception name. 167 | extern NSString * const RKLICURegexException; 168 | 169 | // NSError error domains and user info keys. 170 | extern NSString * const RKLICURegexErrorDomain; 171 | 172 | extern NSString * const RKLICURegexEnumerationOptionsErrorKey; 173 | extern NSString * const RKLICURegexErrorCodeErrorKey; 174 | extern NSString * const RKLICURegexErrorNameErrorKey; 175 | extern NSString * const RKLICURegexLineErrorKey; 176 | extern NSString * const RKLICURegexOffsetErrorKey; 177 | extern NSString * const RKLICURegexPreContextErrorKey; 178 | extern NSString * const RKLICURegexPostContextErrorKey; 179 | extern NSString * const RKLICURegexRegexErrorKey; 180 | extern NSString * const RKLICURegexRegexOptionsErrorKey; 181 | extern NSString * const RKLICURegexReplacedCountErrorKey; 182 | extern NSString * const RKLICURegexReplacedStringErrorKey; 183 | extern NSString * const RKLICURegexReplacementStringErrorKey; 184 | extern NSString * const RKLICURegexSubjectRangeErrorKey; 185 | extern NSString * const RKLICURegexSubjectStringErrorKey; 186 | 187 | @interface NSString (RegexKitLiteAdditions) 188 | 189 | + (void)RKL_METHOD_PREPEND(clearStringCache); 190 | 191 | // Although these are marked as deprecated, a bug in GCC prevents a warning from being issues for + class methods. Filed bug with Apple, #6736857. 192 | + (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex RKL_DEPRECATED_ATTRIBUTE; 193 | + (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex options:(RKLRegexOptions)options error:(NSError **)error RKL_DEPRECATED_ATTRIBUTE; 194 | 195 | - (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex; 196 | - (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex range:(NSRange)range; 197 | - (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error; 198 | 199 | - (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex; 200 | - (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex inRange:(NSRange)range; 201 | - (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error; 202 | 203 | - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex; 204 | - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex capture:(NSInteger)capture; 205 | - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex inRange:(NSRange)range; 206 | - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error; 207 | 208 | - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex; 209 | - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex capture:(NSInteger)capture; 210 | - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex inRange:(NSRange)range; 211 | - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error; 212 | 213 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement; 214 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange; 215 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error; 216 | 217 | //// >= 3.0 218 | 219 | - (NSInteger)RKL_METHOD_PREPEND(captureCount); 220 | - (NSInteger)RKL_METHOD_PREPEND(captureCountWithOptions):(RKLRegexOptions)options error:(NSError **)error; 221 | 222 | - (BOOL)RKL_METHOD_PREPEND(isRegexValid); 223 | - (BOOL)RKL_METHOD_PREPEND(isRegexValidWithOptions):(RKLRegexOptions)options error:(NSError **)error; 224 | 225 | - (void)RKL_METHOD_PREPEND(flushCachedRegexData); 226 | 227 | - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex; 228 | - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex capture:(NSInteger)capture; 229 | - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex range:(NSRange)range; 230 | - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range capture:(NSInteger)capture error:(NSError **)error; 231 | 232 | 233 | - (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex; 234 | - (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range; 235 | - (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error; 236 | 237 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex; 238 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range; 239 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error; 240 | 241 | //// >= 4.0 242 | 243 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 244 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 245 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 246 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList; 247 | 248 | - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count; 249 | 250 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 251 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 252 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... RKL_REQUIRES_NIL_TERMINATION; 253 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList; 254 | 255 | - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count; 256 | 257 | #ifdef _RKL_BLOCKS_ENABLED 258 | 259 | - (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 260 | - (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 261 | 262 | - (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 263 | - (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 264 | 265 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 266 | - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 267 | 268 | #endif // _RKL_BLOCKS_ENABLED 269 | 270 | @end 271 | 272 | @interface NSMutableString (RegexKitLiteAdditions) 273 | 274 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement; 275 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange; 276 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error; 277 | 278 | //// >= 4.0 279 | 280 | #ifdef _RKL_BLOCKS_ENABLED 281 | 282 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 283 | - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block; 284 | 285 | #endif // _RKL_BLOCKS_ENABLED 286 | 287 | @end 288 | 289 | #endif // __OBJC__ 290 | 291 | #endif // _REGEXKITLITE_H_ 292 | 293 | #ifdef __cplusplus 294 | } // extern "C" 295 | #endif 296 | -------------------------------------------------------------------------------- /RegexParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/22/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface RegexParser : NSObject { 11 | NSString* _vowel; 12 | NSString* _consonant; 13 | NSString* _casesensitive; 14 | NSArray* _patterns; 15 | int _maxPatternLength; 16 | } 17 | 18 | + (RegexParser *)sharedInstance; 19 | 20 | - (NSString*)parse:(NSString*)string; 21 | - (BOOL)isVowel:(unichar)c; 22 | - (BOOL)isConsonant:(unichar)c; 23 | - (BOOL)isPunctuation:(unichar)c; 24 | - (BOOL)isCaseSensitive:(unichar)c; 25 | - (BOOL)isExact:(NSString*) needle heystack:(NSString*)heystack start:(int)start end:(int)end not:(BOOL)not; 26 | - (unichar)smallCap:(unichar) letter; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /RegexParser.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/22/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "RegexParser.h" 9 | 10 | static RegexParser* sharedInstance = nil; 11 | 12 | @implementation RegexParser 13 | 14 | + (RegexParser *)sharedInstance { 15 | if (sharedInstance == nil) { 16 | [[self alloc] init]; // assignment not done here, see allocWithZone 17 | } 18 | return sharedInstance; 19 | } 20 | 21 | + (id)allocWithZone:(NSZone *)zone { 22 | 23 | if (sharedInstance == nil) { 24 | sharedInstance = [super allocWithZone:zone]; 25 | return sharedInstance; // assignment and return on first allocation 26 | } 27 | return nil; //on subsequent allocation attempts return nil 28 | } 29 | 30 | - (id)copyWithZone:(NSZone *)zone { 31 | return self; 32 | } 33 | 34 | - (id)retain { 35 | return self; 36 | } 37 | 38 | - (oneway void)release { 39 | //do nothing 40 | } 41 | 42 | - (id)autorelease { 43 | return self; 44 | } 45 | 46 | - (NSUInteger)retainCount { 47 | return NSUIntegerMax; // This is sooo not zero 48 | } 49 | 50 | - (id)init { 51 | self = [super init]; 52 | if (self) { 53 | NSError *error = nil; 54 | NSString *filePath = [[NSBundle mainBundle] pathForResource:@"regex" ofType:@"json"]; 55 | NSData *jsonData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingUncached error: &error]; 56 | 57 | if (jsonData) { 58 | 59 | NSDictionary *jsonArray = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error: &error]; 60 | 61 | if (!jsonArray) { 62 | @throw error; 63 | } else { 64 | _vowel = [[NSString alloc] initWithString:[jsonArray objectForKey:@"vowel"]]; 65 | _consonant = [[NSString alloc] initWithString:[jsonArray objectForKey:@"consonant"]]; 66 | _casesensitive = [[NSString alloc] initWithString:[jsonArray objectForKey:@"casesensitive"]]; 67 | _patterns = [[NSArray alloc] initWithArray:[jsonArray objectForKey:@"patterns"]]; 68 | _maxPatternLength = [[[_patterns objectAtIndex:0] objectForKey:@"find"] length]; 69 | } 70 | 71 | } else { 72 | @throw error; 73 | } 74 | } 75 | return self; 76 | } 77 | 78 | - (void)dealloc { 79 | [_vowel release]; 80 | [_consonant release]; 81 | [_casesensitive release]; 82 | [_patterns release]; 83 | 84 | [super dealloc]; 85 | } 86 | 87 | - (NSString*)parse:(NSString *)string { 88 | if (!string || [string length] == 0) { 89 | return string; 90 | } 91 | 92 | NSString* fixed = [self clean:string]; 93 | NSMutableString* output = [[NSMutableString alloc] initWithCapacity:0]; 94 | 95 | int len = [fixed length], cur; 96 | for(cur = 0; cur < len; ++cur) { 97 | int start = cur, end; 98 | BOOL matched = FALSE; 99 | 100 | int chunkLen; 101 | for(chunkLen = _maxPatternLength; chunkLen > 0; --chunkLen) { 102 | end = start + chunkLen; 103 | if(end <= len) { 104 | NSString* chunk = [fixed substringWithRange:NSMakeRange(start, chunkLen)]; 105 | 106 | // Binary Search 107 | int left = 0, right = [_patterns count] - 1, mid; 108 | while(right >= left) { 109 | mid = (right + left) / 2; 110 | NSDictionary* pattern = [_patterns objectAtIndex:mid]; 111 | NSString* find = [pattern objectForKey:@"find"]; 112 | if([find isEqualToString:chunk]) { 113 | NSArray* rules = [pattern objectForKey:@"rules"]; 114 | for(NSDictionary* rule in rules) { 115 | 116 | BOOL replace = TRUE; 117 | int chk = 0; 118 | NSArray* matches = [rule objectForKey:@"matches"]; 119 | for(NSDictionary* match in matches) { 120 | NSString* value = [match objectForKey:@"value"]; 121 | NSString* type = [match objectForKey:@"type"]; 122 | NSString* scope = [match objectForKey:@"scope"]; 123 | BOOL isNegative = [[match objectForKey:@"negative"] boolValue]; 124 | 125 | if([type isEqualToString:@"suffix"]) { 126 | chk = end; 127 | } 128 | // Prefix 129 | else { 130 | chk = start - 1; 131 | } 132 | 133 | // Beginning 134 | if([scope isEqualToString:@"punctuation"]) { 135 | if( 136 | ! ( 137 | (chk < 0 && [type isEqualToString:@"prefix"]) || 138 | (chk >= len && [type isEqualToString:@"suffix"]) || 139 | [self isPunctuation:[fixed characterAtIndex:chk]] 140 | ) ^ isNegative 141 | ) { 142 | replace = FALSE; 143 | break; 144 | } 145 | } 146 | // Vowel 147 | else if([scope isEqualToString:@"vowel"]) { 148 | if( 149 | ! ( 150 | ( 151 | (chk >= 0 && [type isEqualToString:@"prefix"]) || 152 | (chk < len && [type isEqualToString:@"suffix"]) 153 | ) && 154 | [self isVowel:[fixed characterAtIndex:chk]] 155 | ) ^ isNegative 156 | ) { 157 | replace = FALSE; 158 | break; 159 | } 160 | } 161 | // Consonant 162 | else if([scope isEqualToString:@"consonant"]) { 163 | if( 164 | ! ( 165 | ( 166 | (chk >= 0 && [type isEqualToString:@"prefix"]) || 167 | (chk < len && [type isEqualToString:@"suffix"]) 168 | ) && 169 | [self isConsonant:[fixed characterAtIndex:chk]] 170 | ) ^ isNegative 171 | ) { 172 | replace = FALSE; 173 | break; 174 | } 175 | } 176 | // Exact 177 | else if([scope isEqualToString:@"exact"]) { 178 | int s, e; 179 | if([type isEqualToString:@"suffix"]) { 180 | s = end; 181 | e = end + [value length]; 182 | } 183 | // Prefix 184 | else { 185 | s = start - [value length]; 186 | e = start; 187 | } 188 | if(![self isExact:value heystack:fixed start:s end:e not:isNegative]) { 189 | replace = FALSE; 190 | break; 191 | } 192 | } 193 | } 194 | 195 | if(replace) { 196 | [output appendString:[rule objectForKey:@"replace"]]; 197 | [output appendString:@"(্[যবম])?(্?)([ঃঁ]?)"]; 198 | cur = end - 1; 199 | matched = TRUE; 200 | break; 201 | } 202 | 203 | } 204 | 205 | if(matched == TRUE) break; 206 | 207 | // Default 208 | [output appendString:[pattern objectForKey:@"replace"]]; 209 | [output appendString:@"(্[যবম])?(্?)([ঃঁ]?)"]; 210 | cur = end - 1; 211 | matched = TRUE; 212 | break; 213 | } 214 | else if ([find length] > [chunk length] || 215 | ([find length] == [chunk length] && [find compare:chunk] == NSOrderedAscending)) { 216 | left = mid + 1; 217 | } else { 218 | right = mid - 1; 219 | } 220 | } 221 | if(matched == TRUE) break; 222 | } 223 | } 224 | 225 | if(!matched) { 226 | unichar oldChar = [fixed characterAtIndex:cur]; 227 | [output appendString:[NSString stringWithCharacters:&oldChar length:1]]; 228 | } 229 | // NSLog(@"cur: %s, start: %s, end: %s, prev: %s\n", cur, start, end, prev); 230 | } 231 | 232 | [output autorelease]; 233 | 234 | return output; 235 | } 236 | 237 | - (BOOL)isVowel:(unichar)c { 238 | // Making it lowercase for checking 239 | c = [self smallCap:c]; 240 | int i, len = [_vowel length]; 241 | for (i = 0; i < len; ++i) { 242 | if ([_vowel characterAtIndex:i] == c) { 243 | return TRUE; 244 | } 245 | } 246 | return FALSE; 247 | } 248 | 249 | - (BOOL)isConsonant:(unichar)c { 250 | // Making it lowercase for checking 251 | c = [self smallCap:c]; 252 | int i, len = [_consonant length]; 253 | for (i = 0; i < len; ++i) { 254 | if ([_consonant characterAtIndex:i] == c) { 255 | return TRUE; 256 | } 257 | } 258 | return FALSE; 259 | } 260 | 261 | - (BOOL)isPunctuation:(unichar)c { 262 | return !([self isVowel:c] || [self isConsonant:c]); 263 | } 264 | 265 | - (BOOL)isCaseSensitive:(unichar)c { 266 | // Making it lowercase for checking 267 | c = [self smallCap:c]; 268 | int i, len = [_casesensitive length]; 269 | for (i = 0; i < len; ++i) { 270 | if ([_casesensitive characterAtIndex:i] == c) { 271 | return TRUE; 272 | } 273 | } 274 | return FALSE; 275 | } 276 | 277 | - (BOOL)isExact:(NSString*) needle heystack:(NSString*)heystack start:(int)start end:(int)end not:(BOOL)not { 278 | int len = end - start; 279 | return ((start >= 0 && end < [heystack length] 280 | && [[heystack substringWithRange:NSMakeRange(start, len)] isEqualToString:needle]) ^ not); 281 | } 282 | 283 | - (unichar)smallCap:(unichar) letter { 284 | if(letter >= 'A' && letter <= 'Z') { 285 | letter = letter - 'A' + 'a'; 286 | } 287 | return letter; 288 | } 289 | 290 | - (NSString*)clean:(NSString *)string { 291 | NSMutableString* fixed = [[NSMutableString alloc] initWithCapacity:0]; 292 | int i, len = [string length]; 293 | for (i = 0; i < len; ++i) { 294 | unichar c = [string characterAtIndex:i]; 295 | if (![self isCaseSensitive:c]) { 296 | [fixed appendFormat:@"%C", [self smallCap:c]]; 297 | } 298 | } 299 | [fixed autorelease]; 300 | return fixed; 301 | } 302 | 303 | @end 304 | -------------------------------------------------------------------------------- /Suggestion.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/28/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface Suggestion : NSObject { 11 | NSMutableArray* _suggestions; 12 | } 13 | 14 | + (Suggestion *)sharedInstance; 15 | 16 | - (NSMutableArray*)getList:(NSString*)term; 17 | - (BOOL)isKar:(NSString*)letter; 18 | - (BOOL)isVowel:(NSString*)letter; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Suggestion.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/28/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import "Suggestion.h" 9 | #import "AvroParser.h" 10 | #import "AutoCorrect.h" 11 | #import "RegexParser.h" 12 | #import "Database.h" 13 | #import "NSString+Levenshtein.h" 14 | #import "CacheManager.h" 15 | #import "RegexKitLite.h" 16 | 17 | static Suggestion* sharedInstance = nil; 18 | 19 | @implementation Suggestion 20 | 21 | + (Suggestion *)sharedInstance { 22 | if (sharedInstance == nil) { 23 | [[self alloc] init]; // assignment not done here, see allocWithZone 24 | } 25 | return sharedInstance; 26 | } 27 | 28 | + (id)allocWithZone:(NSZone *)zone { 29 | 30 | if (sharedInstance == nil) { 31 | sharedInstance = [super allocWithZone:zone]; 32 | return sharedInstance; // assignment and return on first allocation 33 | } 34 | return nil; //on subsequent allocation attempts return nil 35 | } 36 | 37 | - (id)copyWithZone:(NSZone *)zone { 38 | return self; 39 | } 40 | 41 | - (id)retain { 42 | return self; 43 | } 44 | 45 | - (oneway void)release { 46 | //do nothing 47 | } 48 | 49 | - (id)autorelease { 50 | return self; 51 | } 52 | 53 | - (NSUInteger)retainCount { 54 | return NSUIntegerMax; // This is sooo not zero 55 | } 56 | 57 | - (id)init { 58 | self = [super init]; 59 | if (self) { 60 | _suggestions = [[NSMutableArray alloc] initWithCapacity:0]; 61 | } 62 | return self; 63 | } 64 | 65 | - (void)dealloc { 66 | [_suggestions release]; 67 | [super dealloc]; 68 | } 69 | 70 | - (NSMutableArray*)getList:(NSString*)term { 71 | if (term && [term length] == 0) { 72 | return _suggestions; 73 | } 74 | 75 | // Suggestions from Default Parser 76 | NSString* paresedString = [[AvroParser sharedInstance] parse:term]; 77 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeDictionary"]) { 78 | // Saving humanity by reducing a few CPU cycles 79 | [_suggestions addObjectsFromArray:[[CacheManager sharedInstance] arrayForKey:term]]; 80 | if (_suggestions && [_suggestions count] == 0) { 81 | // Suggestions form AutoCorrect 82 | NSString* autoCorrect = [[AutoCorrect sharedInstance] find:term]; 83 | if (autoCorrect) { 84 | [_suggestions addObject:autoCorrect]; 85 | } 86 | 87 | // Suggestions from Dictionary 88 | NSArray* dicList = [[Database sharedInstance] find:term]; 89 | if (dicList) { 90 | // Remove autoCorrect if it is already in the dictionary 91 | // PROPOSAL: don't add the autoCorrect, which matches with the dictionary entry 92 | if (autoCorrect && [dicList containsObject:autoCorrect]) { 93 | [_suggestions removeObjectIdenticalTo:autoCorrect]; 94 | } 95 | // Sort dicList based on edit distance 96 | NSArray* sortedDicList = [dicList sortedArrayUsingComparator:^NSComparisonResult(id left, id right) { 97 | int dist1 = [paresedString computeLevenshteinDistanceWithString:(NSString*)left]; 98 | int dist2 = [paresedString computeLevenshteinDistanceWithString:(NSString*)right]; 99 | if (dist1 < dist2) { 100 | return NSOrderedAscending; 101 | } 102 | else if (dist1 > dist2) { 103 | return NSOrderedDescending; 104 | } else { 105 | return NSOrderedSame; 106 | } 107 | }]; 108 | [_suggestions addObjectsFromArray:sortedDicList]; 109 | } 110 | 111 | [[CacheManager sharedInstance] setArray:[[_suggestions copy] autorelease] forKey:term]; 112 | } 113 | 114 | // Suggestions with Suffix 115 | int i; 116 | BOOL alreadySelected = FALSE; 117 | [[CacheManager sharedInstance] removeAllBase]; 118 | for (i = [term length]-1; i > 0; --i) { 119 | NSString* suffix = [[Database sharedInstance] banglaForSuffix:[[term substringFromIndex:i] lowercaseString]]; 120 | if (suffix) { 121 | NSString* base = [term substringToIndex:i]; 122 | NSArray* cached = [[CacheManager sharedInstance] arrayForKey:base]; 123 | NSString* selected; 124 | if (!alreadySelected) { 125 | // Base user selection 126 | selected = [[CacheManager sharedInstance] stringForKey:base]; 127 | } 128 | // This should always exist, so it's just a safety check 129 | if (cached) { 130 | for (NSString *item in cached) { 131 | // Skip AutoCorrect English Entry 132 | if ([base isEqualToString:item]) { 133 | continue; 134 | } 135 | NSString* word; 136 | // Again saving humanity cause I'm Superman, no I'm not drunk or on weed :D 137 | int cutPos = [item length] - 1; 138 | 139 | NSString* itemRMC = [item substringFromIndex:cutPos]; // RMC is Right Most Character 140 | NSString* suffixLMC = [suffix substringToIndex:1]; // LMC is Left Most Character 141 | // BEGIN :: This part was taken from http://d.pr/zTmF 142 | if ([self isVowel:itemRMC] && [self isKar:suffixLMC]) { 143 | word = [NSString stringWithFormat:@"%@\u09df%@", item ,suffix]; 144 | } 145 | else { 146 | if ([itemRMC isEqualToString:@"\u09ce"]) { 147 | word = [NSString stringWithFormat:@"%@\u09a4%@", [item substringToIndex:cutPos], suffix]; 148 | } 149 | else if ([itemRMC isEqualToString:@"\u0982"]) { 150 | word = [NSString stringWithFormat:@"%@\u0999%@", [item substringToIndex:cutPos], suffix]; 151 | } else { 152 | word = [NSString stringWithFormat:@"%@%@", item, suffix]; 153 | } 154 | } 155 | // END 156 | 157 | // Reverse Suffix Caching 158 | [[CacheManager sharedInstance] setBase:[NSArray arrayWithObjects:base, item, nil] forKey:word]; 159 | 160 | // Check that the WORD is not already in the list 161 | if (![_suggestions containsObject:word]) { 162 | // Intelligent Selection 163 | if (!alreadySelected && selected && [item isEqualToString:selected]) { 164 | if (![[CacheManager sharedInstance] stringForKey:term]) { 165 | [[CacheManager sharedInstance] setString:word forKey:term]; 166 | } 167 | alreadySelected = TRUE; 168 | } 169 | [_suggestions addObject:word]; 170 | } 171 | } 172 | } 173 | } 174 | } 175 | } 176 | 177 | if ([_suggestions containsObject:paresedString] == NO) { 178 | [_suggestions addObject:paresedString]; 179 | } 180 | 181 | return _suggestions; 182 | } 183 | 184 | - (BOOL)isKar:(NSString*)letter { 185 | return [letter isMatchedByRegex:@"^[\u09be\u09bf\u09c0\u09c1\u09c2\u09c3\u09c7\u09c8\u09cb\u09cc\u09c4]$"]; 186 | } 187 | 188 | - (BOOL)isVowel:(NSString*)letter { 189 | return [letter isMatchedByRegex:@"^[\u0985\u0986\u0987\u0988\u0989\u098a\u098b\u098f\u0990\u0993\u0994\u098c\u09e1\u09be\u09bf\u09c0\u09c1\u09c2\u09c3\u09c7\u09c8\u09cb\u09cc]$"]; 190 | } 191 | 192 | @end 193 | -------------------------------------------------------------------------------- /avro.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/avro.icns -------------------------------------------------------------------------------- /data.json: -------------------------------------------------------------------------------- 1 | {"patterns":[{"find":"NgkSh","replace":"ঙ্ক্ষ","rules":[]},{"find":"Ngkkh","replace":"ঙ্ক্ষ","rules":[]},{"find":"NGch","replace":"ঞ্ছ","rules":[]},{"find":"NGjh","replace":"ঞ্ঝ","rules":[]},{"find":"Nggh","replace":"ঙ্ঘ","rules":[]},{"find":"Ngkh","replace":"ঙ্খ","rules":[]},{"find":"Ngkx","replace":"ঙ্ক্ষ","rules":[]},{"find":"ShTh","replace":"ষ্ঠ","rules":[]},{"find":"Shph","replace":"ষ্ফ","rules":[]},{"find":"kShN","replace":"ক্ষ্ণ","rules":[]},{"find":"kShm","replace":"ক্ষ্ম","rules":[]},{"find":"kkhN","replace":"ক্ষ্ণ","rules":[]},{"find":"kkhm","replace":"ক্ষ্ম","rules":[]},{"find":"ngOI","replace":"ঙ্গৈ","rules":[]},{"find":"ngOU","replace":"ঙ্গৌ","rules":[]},{"find":"rri`","replace":"ৃ","rules":[]},{"find":"shch","replace":"শ্ছ","rules":[]},{"find":"...","replace":"...","rules":[]},{"find":"Gdh","replace":"গ্ধ","rules":[]},{"find":"Ghn","replace":"ঘ্ন","rules":[]},{"find":"NDh","replace":"ণ্ঢ","rules":[]},{"find":"NGc","replace":"ঞ্চ","rules":[]},{"find":"NGj","replace":"ঞ্জ","rules":[]},{"find":"NGr","replace":"ঞর","rules":[]},{"find":"NTh","replace":"ণ্ঠ","rules":[]},{"find":"Ngg","replace":"ঙ্গ","rules":[]},{"find":"Ngk","replace":"ঙ্ক","rules":[]},{"find":"Ngm","replace":"ঙ্ম","rules":[]},{"find":"Ngr","replace":"ঙর","rules":[]},{"find":"Ngx","replace":"ঙ্ষ","rules":[]},{"find":"OI`","replace":"ৈ","rules":[]},{"find":"OU`","replace":"ৌ","rules":[]},{"find":"Sch","replace":"শ্ছ","rules":[]},{"find":"ShN","replace":"ষ্ণ","rules":[]},{"find":"ShT","replace":"ষ্ট","rules":[]},{"find":"Shf","replace":"ষ্ফ","rules":[]},{"find":"Shk","replace":"ষ্ক","rules":[]},{"find":"Shm","replace":"ষ্ম","rules":[]},{"find":"Shp","replace":"ষ্প","rules":[]},{"find":"bdh","replace":"ব্ধ","rules":[]},{"find":"bhl","replace":"ভ্ল","rules":[]},{"find":"cNG","replace":"চ্ঞ","rules":[]},{"find":"cch","replace":"চ্ছ","rules":[]},{"find":"dbh","replace":"দ্ভ","rules":[]},{"find":"ddh","replace":"দ্ধ","rules":[]},{"find":"dgh","replace":"দ্ঘ","rules":[]},{"find":"dhm","replace":"ধ্ম","rules":[]},{"find":"dhn","replace":"ধ্ন","rules":[]},{"find":"ee`","replace":"ী","rules":[]},{"find":"gdh","replace":"গ্ধ","rules":[]},{"find":"ghn","replace":"ঘ্ন","rules":[]},{"find":"jNG","replace":"জ্ঞ","rules":[]},{"find":"jjh","replace":"জ্ঝ","rules":[]},{"find":"kSh","replace":"ক্ষ","rules":[]},{"find":"kkh","replace":"ক্ষ","rules":[]},{"find":"ksh","replace":"কশ","rules":[]},{"find":"kxN","replace":"ক্ষ্ণ","rules":[]},{"find":"kxm","replace":"ক্ষ্ম","rules":[]},{"find":"lbh","replace":"ল্ভ","rules":[]},{"find":"ldh","replace":"ল্ধ","rules":[]},{"find":"lgh","replace":"লঘ","rules":[]},{"find":"lkh","replace":"লখ","rules":[]},{"find":"lph","replace":"লফ","rules":[]},{"find":"mbh","replace":"ম্ভ","rules":[]},{"find":"mph","replace":"ম্ফ","rules":[]},{"find":"mpl","replace":"মপ্ল","rules":[]},{"find":"mth","replace":"ম্থ","rules":[]},{"find":"nTh","replace":"ন্ঠ","rules":[]},{"find":"nch","replace":"ঞ্ছ","rules":[]},{"find":"ndh","replace":"ন্ধ","rules":[]},{"find":"ngI","replace":"ঙ্গী","rules":[]},{"find":"ngO","replace":"ঙ্গো","rules":[]},{"find":"ngU","replace":"ঙ্গূ","rules":[]},{"find":"nga","replace":"ঙ্গা","rules":[]},{"find":"nge","replace":"ঙ্গে","rules":[]},{"find":"ngh","replace":"ঙ্ঘ","rules":[]},{"find":"ngi","replace":"ঙ্গি","rules":[]},{"find":"ngo","replace":"ঙ্গ","rules":[]},{"find":"ngr","replace":"ংর","rules":[]},{"find":"ngu","replace":"ঙ্গু","rules":[]},{"find":"njh","replace":"ঞ্ঝ","rules":[]},{"find":"nkh","replace":"ঙ্খ","rules":[]},{"find":"nsh","replace":"নশ","rules":[]},{"find":"nth","replace":"ন্থ","rules":[]},{"find":"oo`","replace":"ু","rules":[]},{"find":"phl","replace":"ফ্ল","rules":[]},{"find":"psh","replace":"পশ","rules":[]},{"find":"rrZ","replace":"রর‍্য","rules":[]},{"find":"rri","replace":"ৃ","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"}],"replace":"ঋ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"}],"replace":"ঋ"}]},{"find":"rry","replace":"রর‍্য","rules":[]},{"find":"shc","replace":"শ্চ","rules":[]},{"find":"shl","replace":"শ্ল","rules":[]},{"find":"shm","replace":"শ্ম","rules":[]},{"find":"shn","replace":"শ্ন","rules":[]},{"find":"sht","replace":"শ্ত","rules":[]},{"find":"skh","replace":"স্খ","rules":[]},{"find":"skl","replace":"স্ক্ল","rules":[]},{"find":"sph","replace":"স্ফ","rules":[]},{"find":"spl","replace":"স্প্ল","rules":[]},{"find":"sth","replace":"স্থ","rules":[]},{"find":"t``","replace":"ৎ","rules":[]},{"find":"tth","replace":"ত্থ","rules":[]},{"find":",,","replace":"্‌","rules":[]},{"find":"..","replace":"।।","rules":[]},{"find":".`","replace":".","rules":[]},{"find":":`","replace":":","rules":[]},{"find":"AZ","replace":"অ্যা","rules":[]},{"find":"A`","replace":"া","rules":[]},{"find":"DD","replace":"ড্ড","rules":[]},{"find":"Dh","replace":"ঢ","rules":[]},{"find":"GG","replace":"জ্ঞ","rules":[]},{"find":"GN","replace":"গ্ণ","rules":[]},{"find":"Gg","replace":"জ্ঞ","rules":[]},{"find":"Gh","replace":"ঘ","rules":[]},{"find":"Gl","replace":"গ্ল","rules":[]},{"find":"Gm","replace":"গ্ম","rules":[]},{"find":"Gn","replace":"গ্ন","rules":[]},{"find":"I`","replace":"ী","rules":[]},{"find":"ND","replace":"ণ্ড","rules":[]},{"find":"NG","replace":"ঞ","rules":[]},{"find":"NN","replace":"ণ্ণ","rules":[]},{"find":"NT","replace":"ণ্ট","rules":[]},{"find":"Ng","replace":"ঙ","rules":[]},{"find":"Nm","replace":"ণ্ম","rules":[]},{"find":"Nn","replace":"ণ্ন","rules":[]},{"find":"OI","replace":"ৈ","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"}],"replace":"ঐ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"}],"replace":"ঐ"}]},{"find":"OU","replace":"ৌ","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"}],"replace":"ঔ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"}],"replace":"ঔ"}]},{"find":"O`","replace":"ো","rules":[]},{"find":"Rg","replace":"ড়্গ","rules":[]},{"find":"Rh","replace":"ঢ়","rules":[]},{"find":"Sc","replace":"শ্চ","rules":[]},{"find":"Sc","replace":"শ্চ","rules":[]},{"find":"Sh","replace":"ষ","rules":[]},{"find":"Sl","replace":"শ্ল","rules":[]},{"find":"Sm","replace":"শ্ম","rules":[]},{"find":"Sn","replace":"শ্ন","rules":[]},{"find":"St","replace":"শ্ত","rules":[]},{"find":"TT","replace":"ট্ট","rules":[]},{"find":"Th","replace":"ঠ","rules":[]},{"find":"Tm","replace":"ট্ম","rules":[]},{"find":"U`","replace":"ূ","rules":[]},{"find":"^`","replace":"^","rules":[]},{"find":"aZ","replace":"অ্যা","rules":[]},{"find":"a`","replace":"া","rules":[]},{"find":"bb","replace":"ব্ব","rules":[]},{"find":"bd","replace":"ব্দ","rules":[]},{"find":"bh","replace":"ভ","rules":[]},{"find":"bj","replace":"ব্জ","rules":[]},{"find":"bl","replace":"ব্ল","rules":[]},{"find":"cc","replace":"চ্চ","rules":[]},{"find":"ch","replace":"ছ","rules":[]},{"find":"dd","replace":"দ্দ","rules":[]},{"find":"dg","replace":"দ্গ","rules":[]},{"find":"dh","replace":"ধ","rules":[]},{"find":"dm","replace":"দ্ম","rules":[]},{"find":"dv","replace":"দ্ভ","rules":[]},{"find":"e`","replace":"ে","rules":[]},{"find":"ee","replace":"ী","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"ঈ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"ঈ"}]},{"find":"fl","replace":"ফ্ল","rules":[]},{"find":"gG","replace":"জ্ঞ","rules":[]},{"find":"gN","replace":"গ্ণ","rules":[]},{"find":"gg","replace":"জ্ঞ","rules":[]},{"find":"gh","replace":"ঘ","rules":[]},{"find":"gl","replace":"গ্ল","rules":[]},{"find":"gm","replace":"গ্ম","rules":[]},{"find":"gn","replace":"গ্ন","rules":[]},{"find":"hN","replace":"হ্ণ","rules":[]},{"find":"hl","replace":"হ্ল","rules":[]},{"find":"hm","replace":"হ্ম","rules":[]},{"find":"hn","replace":"হ্ন","rules":[]},{"find":"i`","replace":"ি","rules":[]},{"find":"jh","replace":"ঝ","rules":[]},{"find":"jj","replace":"জ্জ","rules":[]},{"find":"kT","replace":"ক্ট","rules":[]},{"find":"kh","replace":"খ","rules":[]},{"find":"kk","replace":"ক্ক","rules":[]},{"find":"kl","replace":"ক্ল","rules":[]},{"find":"ks","replace":"ক্স","rules":[]},{"find":"kt","replace":"ক্ত","rules":[]},{"find":"kx","replace":"ক্ষ","rules":[]},{"find":"lD","replace":"ল্ড","rules":[]},{"find":"lT","replace":"ল্ট","rules":[]},{"find":"lb","replace":"ল্ব","rules":[]},{"find":"lg","replace":"ল্গ","rules":[]},{"find":"lk","replace":"ল্ক","rules":[]},{"find":"ll","replace":"ল্ল","rules":[]},{"find":"lm","replace":"ল্ম","rules":[]},{"find":"lp","replace":"ল্প","rules":[]},{"find":"lv","replace":"ল্ভ","rules":[]},{"find":"mb","replace":"ম্ব","rules":[]},{"find":"mf","replace":"ম্ফ","rules":[]},{"find":"ml","replace":"ম্ল","rules":[]},{"find":"mm","replace":"ম্ম","rules":[]},{"find":"mn","replace":"ম্ন","rules":[]},{"find":"mp","replace":"ম্প","rules":[]},{"find":"mv","replace":"ম্ভ","rules":[]},{"find":"nD","replace":"ন্ড","rules":[]},{"find":"nT","replace":"ন্ট","rules":[]},{"find":"nc","replace":"ঞ্চ","rules":[]},{"find":"nd","replace":"ন্দ","rules":[]},{"find":"ng","replace":"ং","rules":[]},{"find":"nj","replace":"ঞ্জ","rules":[]},{"find":"nk","replace":"ঙ্ক","rules":[]},{"find":"nm","replace":"ন্ম","rules":[]},{"find":"nn","replace":"ন্ন","rules":[]},{"find":"ns","replace":"ন্স","rules":[]},{"find":"nt","replace":"ন্ত","rules":[]},{"find":"oZ","replace":"অ্য","rules":[]},{"find":"o`","replace":"","rules":[]},{"find":"oo","replace":"ু","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"উ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"উ"}]},{"find":"pT","replace":"প্ট","rules":[]},{"find":"ph","replace":"ফ","rules":[]},{"find":"pl","replace":"প্ল","rules":[]},{"find":"pn","replace":"প্ন","rules":[]},{"find":"pp","replace":"প্প","rules":[]},{"find":"ps","replace":"প্স","rules":[]},{"find":"pt","replace":"প্ত","rules":[]},{"find":"rZ","replace":"র‍্য","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"NO"},{"type":"prefix","scope":"exact","value":"r","negative":"YES"},{"type":"prefix","scope":"exact","value":"y","negative":"YES"},{"type":"prefix","scope":"exact","value":"w","negative":"YES"},{"type":"prefix","scope":"exact","value":"x","negative":"YES"}],"replace":"্র্য"}]},{"find":"rr","replace":"রর","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"suffix","scope":"vowel","value":"","negative":"YES"},{"type":"suffix","scope":"exact","value":"r","negative":"YES"},{"type":"suffix","scope":"punctuation","value":"","negative":"YES"}],"replace":"র্"},{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"NO"},{"type":"prefix","scope":"exact","value":"r","negative":"YES"}],"replace":"্রর"}]},{"find":"ry","replace":"র‍্য","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"NO"},{"type":"prefix","scope":"exact","value":"r","negative":"YES"},{"type":"prefix","scope":"exact","value":"y","negative":"YES"},{"type":"prefix","scope":"exact","value":"w","negative":"YES"},{"type":"prefix","scope":"exact","value":"x","negative":"YES"}],"replace":"্র্য"}]},{"find":"sT","replace":"স্ট","rules":[]},{"find":"sf","replace":"স্ফ","rules":[]},{"find":"sh","replace":"শ","rules":[]},{"find":"sk","replace":"স্ক","rules":[]},{"find":"sl","replace":"স্ল","rules":[]},{"find":"sm","replace":"স্ম","rules":[]},{"find":"sn","replace":"স্ন","rules":[]},{"find":"sp","replace":"স্প","rules":[]},{"find":"st","replace":"স্ত","rules":[]},{"find":"th","replace":"থ","rules":[]},{"find":"tm","replace":"ত্ম","rules":[]},{"find":"tn","replace":"ত্ন","rules":[]},{"find":"tt","replace":"ত্ত","rules":[]},{"find":"u`","replace":"ু","rules":[]},{"find":"vl","replace":"ভ্ল","rules":[]},{"find":"$","replace":"৳","rules":[]},{"find":",","replace":",","rules":[]},{"find":".","replace":"।","rules":[]},{"find":"0","replace":"০","rules":[]},{"find":"1","replace":"১","rules":[]},{"find":"2","replace":"২","rules":[]},{"find":"3","replace":"৩","rules":[]},{"find":"4","replace":"৪","rules":[]},{"find":"5","replace":"৫","rules":[]},{"find":"6","replace":"৬","rules":[]},{"find":"7","replace":"৭","rules":[]},{"find":"8","replace":"৮","rules":[]},{"find":"9","replace":"৯","rules":[]},{"find":":","replace":"ঃ","rules":[]},{"find":"D","replace":"ড","rules":[]},{"find":"G","replace":"গ","rules":[]},{"find":"I","replace":"ী","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"ঈ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"ঈ"}]},{"find":"J","replace":"জ","rules":[]},{"find":"N","replace":"ণ","rules":[]},{"find":"O","replace":"ো","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"}],"replace":"ও"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"}],"replace":"ও"}]},{"find":"R","replace":"ড়","rules":[]},{"find":"S","replace":"শ","rules":[]},{"find":"T","replace":"ট","rules":[]},{"find":"U","replace":"ূ","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"ঊ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"ঊ"}]},{"find":"Y","replace":"য়","rules":[]},{"find":"Z","replace":"্য","rules":[]},{"find":"^","replace":"ঁ","rules":[]},{"find":"`","replace":"","rules":[]},{"find":"a","replace":"া","rules":[{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"আ"},{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"prefix","scope":"exact","value":"a","negative":"YES"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"য়া"},{"matches":[{"type":"prefix","scope":"exact","value":"a","negative":"NO"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"আ"}]},{"find":"b","replace":"ব","rules":[]},{"find":"c","replace":"চ","rules":[]},{"find":"d","replace":"দ","rules":[]},{"find":"e","replace":"ে","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"এ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"এ"}]},{"find":"f","replace":"ফ","rules":[]},{"find":"g","replace":"গ","rules":[]},{"find":"h","replace":"হ","rules":[]},{"find":"i","replace":"ি","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"ই"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"ই"}]},{"find":"j","replace":"জ","rules":[]},{"find":"k","replace":"ক","rules":[]},{"find":"l","replace":"ল","rules":[]},{"find":"m","replace":"ম","rules":[]},{"find":"n","replace":"ন","rules":[]},{"find":"o","replace":"","rules":[{"matches":[{"type":"prefix","scope":"vowel","value":"","negative":"NO"},{"type":"prefix","scope":"exact","value":"o","negative":"YES"}],"replace":"ও"},{"matches":[{"type":"prefix","scope":"vowel","value":"","negative":"NO"},{"type":"prefix","scope":"exact","value":"o","negative":"NO"}],"replace":"অ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"}],"replace":"অ"}]},{"find":"p","replace":"প","rules":[]},{"find":"q","replace":"ক","rules":[]},{"find":"r","replace":"র","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"NO"},{"type":"prefix","scope":"exact","value":"r","negative":"YES"},{"type":"prefix","scope":"exact","value":"y","negative":"YES"},{"type":"prefix","scope":"exact","value":"w","negative":"YES"},{"type":"prefix","scope":"exact","value":"x","negative":"YES"},{"type":"prefix","scope":"exact","value":"Z","negative":"YES"}],"replace":"্র"}]},{"find":"s","replace":"স","rules":[]},{"find":"t","replace":"ত","rules":[]},{"find":"u","replace":"ু","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"উ"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"},{"type":"suffix","scope":"exact","value":"`","negative":"YES"}],"replace":"উ"}]},{"find":"v","replace":"ভ","rules":[]},{"find":"w","replace":"ও","rules":[{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"},{"type":"suffix","scope":"vowel","value":"","negative":"NO"}],"replace":"ওয়"},{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"NO"}],"replace":"্ব"}]},{"find":"x","replace":"ক্স","rules":[{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"}],"replace":"এক্স"}]},{"find":"y","replace":"্য","rules":[{"matches":[{"type":"prefix","scope":"consonant","value":"","negative":"YES"},{"type":"prefix","scope":"punctuation","value":"","negative":"YES"}],"replace":"য়"},{"matches":[{"type":"prefix","scope":"punctuation","value":"","negative":"NO"}],"replace":"ইয়"}]},{"find":"z","replace":"য","rules":[]}],"vowel":"aeiou","consonant":"bcdfghjklmnpqrstvwxyz","casesensitive":"oiudgjnrstyz"} -------------------------------------------------------------------------------- /database.db3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omicronlab/iAvro/107a73283ac34a33faf7f6799c2e9dbfa97e6643/database.db3 -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvroKeyboard 3 | // 4 | // Created by Rifat Nabi on 6/21/12. 5 | // Copyright (c) 2012 OmicronLab. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | #import "AvroParser.h" 12 | #import "Suggestion.h" 13 | #import "Candidates.h" 14 | 15 | //Each input method needs a unique connection name. 16 | //Note that periods and spaces are not allowed in the connection name. 17 | const NSString* kConnectionName = @"Avro_Keyboard_Connection"; 18 | 19 | //let this be a global so our application controller delegate can access it easily 20 | IMKServer* server; 21 | 22 | int main(int argc, char *argv[]) { 23 | 24 | NSString* identifier; 25 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 26 | 27 | [AvroParser sharedInstance]; 28 | [Suggestion sharedInstance]; 29 | 30 | //find the bundle identifier and then initialize the input method server 31 | identifier = [[NSBundle mainBundle] bundleIdentifier]; 32 | server = [[IMKServer alloc] initWithName:(NSString*)kConnectionName bundleIdentifier:identifier]; 33 | [Candidates allocateSharedInstanceWithServer:server]; 34 | 35 | //load the bundle explicitly because in this case the input method is a background only application 36 | [NSBundle loadNibNamed:@"MainMenu" owner:[NSApplication sharedApplication]]; 37 | 38 | //finally run everything 39 | [[NSApplication sharedApplication] run]; 40 | 41 | [Candidates deallocateSharedInstance]; 42 | [server release]; 43 | [pool release]; 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /preferences.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CandidatePanelType 6 | 1 7 | CommitNewLineOnEnter 8 | 9 | IncludeDictionary 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /regex.json: -------------------------------------------------------------------------------- 1 | { 2 | "patterns": [ 3 | { 4 | "find": "chchh", 5 | "replace": "((চ্ছ)|((চ|ছ|([চছ](্?)(হ|ঃ|(হ্‌?))))(্?)(((চ|ছ|([চছ](্?)(হ|ঃ|(হ্‌?))))(্?)((হ|ঃ|(হ্‌?)))?)|([চছ](্?)((হ|ঃ|(হ্‌?))(্?)(হ|ঃ|(হ্‌?))))|([চছ](্?)(হ|ঃ|(হ্‌?))(্?)(হ|ঃ|(হ্‌?)))))|((চ|ছ|([চছ](্?)(হ|ঃ|(হ্‌?))))(্?)(চ|ছ|([চছ](্?)(হ|ঃ|(হ্‌?))))(্?)(হ|ঃ|(হ্‌?)))|([চছ](্?)(হ|ঃ|(হ্‌?))(্?)[চছ](্?)(হ|ঃ|(হ্‌?))(্?)(হ|ঃ|(হ্‌?))))", 6 | "rules": [] 7 | }, 8 | { 9 | "find": "ngkkh", 10 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)((ক্ষ)|((ক(্?)ক?)(্?)(হ|ঃ|(হ্‌?)))|(ক?(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?)))))|(ক(্?)ক(্?)(হ|ঃ|(হ্‌?)))))|((((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)ক)|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ক))(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?)))))|((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)ক(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?)))))|((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)(ক(্?)ক?)(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ক(্?)ক(্?)(হ|ঃ|(হ্‌?))))", 11 | "rules": [] 12 | }, 13 | { 14 | "find": "ngksh", 15 | "replace": "(((((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)ক)|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ক))(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))|((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)ক(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))|((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)((ক(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))|((ক(্?)[সশষ])(্?)(হ|ঃ|(হ্‌?)))|(ক(্?)[সশষ](্?)(হ|ঃ|(হ্‌?)))))|((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)(ক(্?)[সশষ])(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ক(্?)[সশষ](্?)(হ|ঃ|(হ্‌?))))", 16 | "rules": [] 17 | }, 18 | { 19 | "find": "kkhm", 20 | "replace": "((((ক্ষ)|((ক(্?)ক?)(্?)(হ|ঃ|(হ্‌?)))|(ক?(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?)))))|(ক(্?)ক(্?)(হ|ঃ|(হ্‌?))))(্?)ম)|((ক(্?)ক?)(্?)((হ|ঃ|(হ্‌?))(্?)ম))|(ক(্?)ক(্?)(হ|ঃ|(হ্‌?))(্?)ম)|(ক(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?))))(্?)ম))", 21 | "rules": [] 22 | }, 23 | { 24 | "find": "kkhn", 25 | "replace": "((((ক্ষ)|((ক(্?)ক?)(্?)(হ|ঃ|(হ্‌?)))|(ক?(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?)))))|(ক(্?)ক(্?)(হ|ঃ|(হ্‌?))))(্?)[নণঁঙঞং])|((ক(্?)ক?)(্?)((হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))|(ক(্?)ক(্?)(হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং])|(ক(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?))))(্?)[নণঁঙঞং]))", 26 | "rules": [] 27 | }, 28 | { 29 | "find": "kshm", 30 | "replace": "((((ক(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))|((ক(্?)[সশষ])(্?)(হ|ঃ|(হ্‌?)))|(ক(্?)[সশষ](্?)(হ|ঃ|(হ্‌?))))(্?)ম)|(ক(্?)(((স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?))))(্?)ম)|([সশষ](্?)((হ|ঃ|(হ্‌?))(্?)ম))|([সশষ](্?)(হ|ঃ|(হ্‌?))(্?)ম)))|((ক(্?)[সশষ])(্?)((হ|ঃ|(হ্‌?))(্?)ম))|(ক(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?))))(্?)ম)|(ক(্?)[সশষ](্?)(হ|ঃ|(হ্‌?))(্?)ম))", 31 | "rules": [] 32 | }, 33 | { 34 | "find": "kshn", 35 | "replace": "((((ক(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))|((ক(্?)[সশষ])(্?)(হ|ঃ|(হ্‌?)))|(ক(্?)[সশষ](্?)(হ|ঃ|(হ্‌?))))(্?)[নণঁঙঞং])|(ক(্?)(((স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?))))(্?)[নণঁঙঞং])|([সশষ](্?)((হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))|([সশষ](্?)(হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং])))|((ক(্?)[সশষ])(্?)((হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))|(ক(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?))))(্?)[নণঁঙঞং])|(ক(্?)[সশষ](্?)(হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))", 36 | "rules": [] 37 | }, 38 | { 39 | "find": "ngch", 40 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)(চ|ছ|([চছ](্?)(হ|ঃ|(হ্‌?)))))|((((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)[চছ])|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)[চছ]))(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)[চছ](্?)(হ|ঃ|(হ্‌?))))", 41 | "rules": [] 42 | }, 43 | { 44 | "find": "nggh", 45 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)(ঘ|((গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?)))))|([নণঁঙঞং](্?)((জ্ঞ)|((গ|(জ্ঞ))(্?)((গ|(জ্ঞ)))?))(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)((((জ্ঞ)|((গ|(জ্ঞ))(্?)((গ|(জ্ঞ)))?))(্?)(হ|ঃ|(হ্‌?)))|((গ|(জ্ঞ))(্?)(ঘ|((গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?)))))|((গ|(জ্ঞ))(্?)(গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?)))))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)(গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?)))|((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)(গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?)))|(((ঙ্গ)|((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)(গ|(জ্ঞ)))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)(গ|(জ্ঞ))))(্?)(হ|ঃ|(হ্‌?))))", 46 | "rules": [] 47 | }, 48 | { 49 | "find": "ngjh", 50 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)(ঝ|(([জয]|(জ়))(্?)(হ|ঃ|(হ্‌?)))))|((((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)([জয]|(জ়)))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)([জয]|(জ়))))(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)([জয]|(জ়))(্?)(হ|ঃ|(হ্‌?))))", 51 | "rules": [] 52 | }, 53 | { 54 | "find": "ngkh", 55 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?)))))|((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)ক(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ক(্?)(হ|ঃ|(হ্‌?)))|((((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)ক)|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ক))(্?)(হ|ঃ|(হ্‌?))))", 56 | "rules": [] 57 | }, 58 | { 59 | "find": "ngkx", 60 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)((ক্ষ)|(ক(্?)((ক্স)|(এক্স)|ষ))))|((((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)ক)|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ক))(্?)((ক্স)|(এক্স)|ষ))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)((ক্ষ)|(ক(্?)((ক্স)|(এক্স)|ষ))))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ক(্?)((ক্স)|(এক্স)|ষ)))", 61 | "rules": [] 62 | }, 63 | { 64 | "find": "shsh", 65 | "replace": "((((স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))?(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))|([সশষ](্?)(হ|ঃ|(হ্‌?))(্?)[সশষ](্?)(হ|ঃ|(হ্‌?))))", 66 | "rules": [] 67 | }, 68 | { 69 | "find": "thth", 70 | "replace": "((ত্থ)|(((থ|ঠ|([তটৎ](্?)(হ|ঃ|(হ্‌?)))))?(্?)(থ|ঠ|([তটৎ](্?)(হ|ঃ|(হ্‌?)))))|([তটৎ](্?)(হ|ঃ|(হ্‌?))(্?)[তটৎ](্?)(হ|ঃ|(হ্‌?))))", 71 | "rules": [] 72 | }, 73 | { 74 | "find": "bdh", 75 | "replace": "((ব(্?)(ধ|ঢ|([দড](্?)(হ|ঃ|(হ্‌?)))))|((ব(্?)[দড])(্?)(হ|ঃ|(হ্‌?)))|(ব(্?)[দড](্?)(হ|ঃ|(হ্‌?))))", 76 | "rules": [] 77 | }, 78 | { 79 | "find": "bhl", 80 | "replace": "(((ভ|(ব(্?)(হ|ঃ|(হ্‌?))))(্?)ল)|(ব(্?)((হ|ঃ|(হ্‌?))(্?)ল))|(ব(্?)(হ|ঃ|(হ্‌?))(্?)ল))", 81 | "rules": [] 82 | }, 83 | { 84 | "find": "cch", 85 | "replace": "(([চছ](্?)(চ|ছ|([চছ](্?)(হ|ঃ|(হ্‌?)))))|([চছ](্?)[চছ](্?)(হ|ঃ|(হ্‌?))))", 86 | "rules": [] 87 | }, 88 | { 89 | "find": "chh", 90 | "replace": "(((চ|ছ|([চছ](্?)(হ|ঃ|(হ্‌?))))(্?)((হ|ঃ|(হ্‌?)))?)|([চছ](্?)((হ|ঃ|(হ্‌?))(্?)(হ|ঃ|(হ্‌?))))|([চছ](্?)(হ|ঃ|(হ্‌?))(্?)(হ|ঃ|(হ্‌?))))", 91 | "rules": [] 92 | }, 93 | { 94 | "find": "cng", 95 | "replace": "((চ্ঞ)|([চছ](্?)(ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ)))))|([চছ](্?)[নণঁঙঞং](্?)(গ|(জ্ঞ)))|(([চছ](্?)[নণঁঙঞং])(্?)(গ|(জ্ঞ))))", 96 | "rules": [] 97 | }, 98 | { 99 | "find": "dbh", 100 | "replace": "(([দড](্?)(ভ|(ব(্?)(হ|ঃ|(হ্‌?)))))|(([দড](্?)ব)(্?)(হ|ঃ|(হ্‌?)))|([দড](্?)ব(্?)(হ|ঃ|(হ্‌?))))", 101 | "rules": [] 102 | }, 103 | { 104 | "find": "ddh", 105 | "replace": "(([দড]?(্?)([দড](্?)(গ|(জ্ঞ))))|(([দড](্?)[দড]?)(্?)(হ|ঃ|(হ্‌?)))|([দড](্?)[দড](্?)(হ|ঃ|(হ্‌?))))", 106 | "rules": [] 107 | }, 108 | { 109 | "find": "dgh", 110 | "replace": "(([দড](্?)(ঘ|((গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?)))))|(([দড](্?)(গ|(জ্ঞ)))(্?)(হ|ঃ|(হ্‌?)))|([দড](্?)(গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?))))", 111 | "rules": [] 112 | }, 113 | { 114 | "find": "dhm", 115 | "replace": "((([দড](্?)(গ|(জ্ঞ)))(্?)ম)|([দড](্?)((হ|ঃ|(হ্‌?))(্?)ম))|([দড](্?)(হ|ঃ|(হ্‌?))(্?)ম))", 116 | "rules": [] 117 | }, 118 | { 119 | "find": "dhn", 120 | "replace": "(((ধ|ঢ|([দড](্?)(হ|ঃ|(হ্‌?))))(্?)[নণঁঙঞং])|([দড](্?)((হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))|([দড](্?)(হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))", 121 | "rules": [] 122 | }, 123 | { 124 | "find": "gdh", 125 | "replace": "(((গ|(জ্ঞ))(্?)(ধ|ঢ|([দড](্?)(হ|ঃ|(হ্‌?)))))|((গ|(জ্ঞ))(্?)[দড](্?)(হ|ঃ|(হ্‌?))))", 126 | "rules": [] 127 | }, 128 | { 129 | "find": "ggh", 130 | "replace": "((((জ্ঞ)|((গ|(জ্ঞ))(্?)((গ|(জ্ঞ)))?))(্?)(হ|ঃ|(হ্‌?)))|((গ|(জ্ঞ))(্?)(ঘ|((গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?)))))|((গ|(জ্ঞ))(্?)(গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?))))", 131 | "rules": [] 132 | }, 133 | { 134 | "find": "ghn", 135 | "replace": "(((ঘ|((গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?))))(্?)[নণঁঙঞং])|((গ|(জ্ঞ))(্?)((হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))|((গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))", 136 | "rules": [] 137 | }, 138 | { 139 | "find": "jjh", 140 | "replace": "(((([জয]|(জ়)))?(্?)(ঝ|(([জয]|(জ়))(্?)(হ|ঃ|(হ্‌?)))))|(হ্য)|(((হ্য)|(([জয]|(জ়))(্?)(([জয]|(জ়)))?))(্?)(হ|ঃ|(হ্‌?)))|(([জয]|(জ়))(্?)([জয]|(জ়))(্?)(হ|ঃ|(হ্‌?))))", 141 | "rules": [] 142 | }, 143 | { 144 | "find": "jng", 145 | "replace": "((জ্ঞ)|(([জয]|(জ়))(্?)(ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ)))))|(([জয]|(জ়))(্?)[নণঁঙঞং](্?)(গ|(জ্ঞ))))", 146 | "rules": [] 147 | }, 148 | { 149 | "find": "kkh", 150 | "replace": "((ক্ষ)|((ক(্?)ক?)(্?)(হ|ঃ|(হ্‌?)))|(ক?(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?)))))|(ক(্?)ক(্?)(হ|ঃ|(হ্‌?))))", 151 | "rules": [] 152 | }, 153 | { 154 | "find": "ksh", 155 | "replace": "((ক(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))|((ক(্?)[সশষ])(্?)(হ|ঃ|(হ্‌?)))|(ক(্?)[সশষ](্?)(হ|ঃ|(হ্‌?))))", 156 | "rules": [] 157 | }, 158 | { 159 | "find": "kxm", 160 | "replace": "((((ক্ষ)|(ক(্?)((ক্স)|(এক্স)|ষ)))(্?)ম)|(ক(্?)(((ক্স)|(এক্স)|ষ)(্?)ম))|(ক(্?)((ক্স)|(এক্স)|ষ)(্?)ম))", 161 | "rules": [] 162 | }, 163 | { 164 | "find": "kxn", 165 | "replace": "((((ক্ষ)|(ক(্?)((ক্স)|(এক্স)|ষ)))(্?)[নণঁঙঞং])|(ক(্?)(((ক্স)|(এক্স)|ষ)(্?)[নণঁঙঞং]))|(ক(্?)((ক্স)|(এক্স)|ষ)(্?)[নণঁঙঞং]))", 166 | "rules": [] 167 | }, 168 | { 169 | "find": "lbh", 170 | "replace": "((ল(্?)(ভ|(ব(্?)(হ|ঃ|(হ্‌?)))))|((ল(্?)ব)(্?)(হ|ঃ|(হ্‌?)))|(ল(্?)ব(্?)(হ|ঃ|(হ্‌?))))", 171 | "rules": [] 172 | }, 173 | { 174 | "find": "ldh", 175 | "replace": "((ল(্?)(ধ|ঢ|([দড](্?)(হ|ঃ|(হ্‌?)))))|((ল(্?)[দড])(্?)(হ|ঃ|(হ্‌?)))|(ল(্?)[দড](্?)(হ|ঃ|(হ্‌?))))", 176 | "rules": [] 177 | }, 178 | { 179 | "find": "lgh", 180 | "replace": "((ল(্?)(ঘ|((গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?)))))|((ল(্?)(গ|(জ্ঞ)))(্?)(হ|ঃ|(হ্‌?)))|(ল(্?)(গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?))))", 181 | "rules": [] 182 | }, 183 | { 184 | "find": "lkh", 185 | "replace": "((ল(্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?)))))|((ল(্?)ক)(্?)(হ|ঃ|(হ্‌?)))|(ল(্?)ক(্?)(হ|ঃ|(হ্‌?))))", 186 | "rules": [] 187 | }, 188 | { 189 | "find": "lph", 190 | "replace": "((ল(্?)(ফ|(প(্?)(হ|ঃ|(হ্‌?)))))|((ল(্?)প)(্?)(হ|ঃ|(হ্‌?)))|(ল(্?)প(্?)(হ|ঃ|(হ্‌?))))", 191 | "rules": [] 192 | }, 193 | { 194 | "find": "mbh", 195 | "replace": "((ম(্?)(ভ|(ব(্?)(হ|ঃ|(হ্‌?)))))|((ম(্?)ব)(্?)(হ|ঃ|(হ্‌?)))|(ম(্?)ব(্?)(হ|ঃ|(হ্‌?))))", 196 | "rules": [] 197 | }, 198 | { 199 | "find": "mph", 200 | "replace": "((ম(্?)(ফ|(প(্?)(হ|ঃ|(হ্‌?)))))|((ম(্?)প)(্?)(হ|ঃ|(হ্‌?)))|(ম(্?)প(্?)(হ|ঃ|(হ্‌?))))", 201 | "rules": [] 202 | }, 203 | { 204 | "find": "mth", 205 | "replace": "((ম(্?)(থ|ঠ|([তটৎ](্?)(হ|ঃ|(হ্‌?)))))|((ম(্?)[তটৎ])(্?)(হ|ঃ|(হ্‌?)))|(ম(্?)[তটৎ](্?)(হ|ঃ|(হ্‌?))))", 206 | "rules": [] 207 | }, 208 | { 209 | "find": "nch", 210 | "replace": "(([নণঁঙঞং](্?)(চ|ছ|([চছ](্?)(হ|ঃ|(হ্‌?)))))|(((ঞ্চ)|([নণঁঙঞং](্?)[চছ]))(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)[চছ](্?)(হ|ঃ|(হ্‌?))))", 211 | "rules": [] 212 | }, 213 | { 214 | "find": "ndh", 215 | "replace": "(([নণঁঙঞং](্?)(ধ|ঢ|([দড](্?)(হ|ঃ|(হ্‌?)))))|(([নণঁঙঞং](্?)[দড])(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)[দড](্?)(হ|ঃ|(হ্‌?))))", 216 | "rules": [] 217 | }, 218 | { 219 | "find": "ngc", 220 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)[চছ])|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)[চছ]))", 221 | "rules": [] 222 | }, 223 | { 224 | "find": "ngg", 225 | "replace": "((ঙ্গ)|((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)(গ|(জ্ঞ)))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)(গ|(জ্ঞ))))", 226 | "rules": [] 227 | }, 228 | { 229 | "find": "ngh", 230 | "replace": "((ঙ্ঘ)|([নণঁঙঞং](্?)(ঘ|((গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?)))))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?))))", 231 | "rules": [] 232 | }, 233 | { 234 | "find": "ngj", 235 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)([জয]|(জ়)))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)([জয]|(জ়))))", 236 | "rules": [] 237 | }, 238 | { 239 | "find": "ngk", 240 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)ক)|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ক))", 241 | "rules": [] 242 | }, 243 | { 244 | "find": "ngm", 245 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)ম)|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)ম))", 246 | "rules": [] 247 | }, 248 | { 249 | "find": "ngx", 250 | "replace": "(((ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))(্?)((ক্স)|(এক্স)|ষ))|([নণঁঙঞং](্?)(গ|(জ্ঞ))(্?)((ক্স)|(এক্স)|ষ)))", 251 | "rules": [] 252 | }, 253 | { 254 | "find": "njh", 255 | "replace": "(([নণঁঙঞং](্?)(ঝ|(([জয]|(জ়))(্?)(হ|ঃ|(হ্‌?)))))|(((ঞ্জ)|([নণঁঙঞং](্?)([জয]|(জ়))))(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)([জয]|(জ়))(্?)(হ|ঃ|(হ্‌?))))", 256 | "rules": [] 257 | }, 258 | { 259 | "find": "nkh", 260 | "replace": "(([নণঁঙঞং](্?)(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?)))))|(((ঙ্ক)|([নণঁঙঞং](্?)ক))(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)ক(্?)(হ|ঃ|(হ্‌?))))", 261 | "rules": [] 262 | }, 263 | { 264 | "find": "nsh", 265 | "replace": "(([নণঁঙঞং](্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))|([নণঁঙঞং](্?)[সশষ](্?)(হ|ঃ|(হ্‌?))))", 266 | "rules": [] 267 | }, 268 | { 269 | "find": "nth", 270 | "replace": "(([নণঁঙঞং](্?)(থ|ঠ|([তটৎ](্?)(হ|ঃ|(হ্‌?)))))|(([নণঁঙঞং](্?)[তটৎ])(্?)(হ|ঃ|(হ্‌?)))|([নণঁঙঞং](্?)[তটৎ](্?)(হ|ঃ|(হ্‌?))))", 271 | "rules": [] 272 | }, 273 | { 274 | "find": "phl", 275 | "replace": "(((ফ|(প(্?)(হ|ঃ|(হ্‌?))))(্?)ল)|(প(্?)((হ|ঃ|(হ্‌?))(্?)ল))|(প(্?)(হ|ঃ|(হ্‌?))(্?)ল))", 276 | "rules": [] 277 | }, 278 | { 279 | "find": "rri", 280 | "replace": "(ঋ|ৃ|(([রড়ঢ়]|(হ্র))([রড়ঢ়]|(হ্র))([ইঈিী]|(য়[িী]))))", 281 | "rules": [] 282 | }, 283 | { 284 | "find": "shm", 285 | "replace": "(((স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?))))(্?)ম)|([সশষ](্?)((হ|ঃ|(হ্‌?))(্?)ম))|([সশষ](্?)(হ|ঃ|(হ্‌?))(্?)ম))", 286 | "rules": [] 287 | }, 288 | { 289 | "find": "shn", 290 | "replace": "(((স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?))))(্?)[নণঁঙঞং])|([সশষ](্?)((হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))|([সশষ](্?)(হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং]))", 291 | "rules": [] 292 | }, 293 | { 294 | "find": "ssh", 295 | "replace": "(([সশষ]?(্?)(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?)))))|([সশষ](্?)[সশষ](্?)(হ|ঃ|(হ্‌?))))", 296 | "rules": [] 297 | }, 298 | { 299 | "find": "t``", 300 | "replace": "ৎ", 301 | "rules": [] 302 | }, 303 | { 304 | "find": "tth", 305 | "replace": "(([তটৎ]?(্?)(থ|ঠ|([তটৎ](্?)(হ|ঃ|(হ্‌?)))))|(([তটৎ](্?)[তটৎ]?)(্?)(হ|ঃ|(হ্‌?)))|([তটৎ](্?)[তটৎ](্?)(হ|ঃ|(হ্‌?))))", 306 | "rules": [] 307 | }, 308 | { 309 | "find": "tth", 310 | "replace": "(([তটৎ]?(্?)(থ|ঠ|([তটৎ](্?)(হ|ঃ|(হ্‌?)))))|(([তটৎ](্?)[তটৎ]?)(্?)(হ|ঃ|(হ্‌?)))|([তটৎ](্?)[তটৎ](্?)(হ|ঃ|(হ্‌?))))", 311 | "rules": [] 312 | }, 313 | { 314 | "find": "zzh", 315 | "replace": "((হ্য)|((জ|য|(জ়)|([‍‌]?্য))(্?)(ঝ|(([জয]|(জ়))(্?)(হ|ঃ|(হ্‌?)))))|(((হ্য)|((জ|য|(জ়)|([‍‌]?্য))(্?)((জ|য|(জ়)|([‍‌]?্য)))?))(্?)(হ|ঃ|(হ্‌?)))|((জ|য|(জ়)|([‍‌]?্য))(্?)(জ|য|(জ়)|([‍‌]?্য))(্?)(হ|ঃ|(হ্‌?))))", 316 | "rules": [] 317 | }, 318 | { 319 | "find": "aa", 320 | "replace": "(আ|(য়া)|া|((([অএ]্যা?)|[আএ]|([‍‌]?(্য)?া)|(য়া))((([অএ]্যা?)|[আএ]|([‍‌]?(্য)?া)|(য়া)))?))", 321 | "rules": [] 322 | }, 323 | { 324 | "find": "ai", 325 | "replace": "(ঐ|ৈ|(([অএ]্যা?)|[আএ]|([‍‌]?(্য)?া)|(য়া))([ইঈিী]|(য়[িী])))", 326 | "rules": [] 327 | }, 328 | { 329 | "find": "au", 330 | "replace": "(ঔ|ৌ(([অএ]্যা?)|[আএ]|([‍‌]?(্য)?া)|(য়া))([উঊুূ]|(য়[ুূ])))", 331 | "rules": [] 332 | }, 333 | { 334 | "find": "az", 335 | "replace": "((([অএ]্যা?)|[আএ]|([‍‌]?(্য)?া)|(য়া))((জ|য|(জ়)|([‍‌]?্য)))?)", 336 | "rules": [] 337 | }, 338 | { 339 | "find": "bb", 340 | "replace": "(ব(্?)ব?)", 341 | "rules": [] 342 | }, 343 | { 344 | "find": "bd", 345 | "replace": "(ব(্?)[দড])", 346 | "rules": [] 347 | }, 348 | { 349 | "find": "bh", 350 | "replace": "(ভ|(ব(্?)(হ|ঃ|(হ্‌?))))", 351 | "rules": [] 352 | }, 353 | { 354 | "find": "bv", 355 | "replace": "(ব?(্?)ভ)", 356 | "rules": [] 357 | }, 358 | { 359 | "find": "cc", 360 | "replace": "([চছ](্?)[চছ]?)", 361 | "rules": [] 362 | }, 363 | { 364 | "find": "ch", 365 | "replace": "(চ|ছ|([চছ](্?)(হ|ঃ|(হ্‌?))))", 366 | "rules": [] 367 | }, 368 | { 369 | "find": "ck", 370 | "replace": "(ক|([চছ](্?)ক))", 371 | "rules": [] 372 | }, 373 | { 374 | "find": "cn", 375 | "replace": "([চছ](্?)[নণঁঙঞং])", 376 | "rules": [] 377 | }, 378 | { 379 | "find": "db", 380 | "replace": "([দড](্?)ব)", 381 | "rules": [] 382 | }, 383 | { 384 | "find": "dd", 385 | "replace": "([দড](্?)[দড]?)", 386 | "rules": [] 387 | }, 388 | { 389 | "find": "dg", 390 | "replace": "([দড](্?)(গ|(জ্ঞ)))", 391 | "rules": [] 392 | }, 393 | { 394 | "find": "dh", 395 | "replace": "(ধ|ঢ|([দড](্?)(হ|ঃ|(হ্‌?))))", 396 | "rules": [] 397 | }, 398 | { 399 | "find": "ee", 400 | "replace": "(ই|ঈ|ি|ী|(য়েই)|(((এ্যা?)|[এে]|([‍‌]?(্য)া)|(য়ে))((এ্যা?)|[এে]|([‍‌]?(্য)া)|(য়ে))))", 401 | "rules": [] 402 | }, 403 | { 404 | "find": "ey", 405 | "replace": "(এ|ই|ে|(েই)|(এই)|ঈ|ী|(((এ্যা?)|[এে]|([‍‌]?(্য)া)|(য়ে))(য়|(ইয়)|([‍‌]?্য))))", 406 | "rules": [] 407 | }, 408 | { 409 | "find": "ff", 410 | "replace": "(ফ(্?)ফ?)", 411 | "rules": [] 412 | }, 413 | { 414 | "find": "gg", 415 | "replace": "((জ্ঞ)|((গ|(জ্ঞ))(্?)((গ|(জ্ঞ)))?))", 416 | "rules": [] 417 | }, 418 | { 419 | "find": "gh", 420 | "replace": "(ঘ|((গ|(জ্ঞ))(্?)(হ|ঃ|(হ্‌?))))", 421 | "rules": [] 422 | }, 423 | { 424 | "find": "hh", 425 | "replace": "((হ|ঃ|(হ্‌?))(্?)(হ|ঃ|(হ্‌?)))", 426 | "rules": [] 427 | }, 428 | { 429 | "find": "hl", 430 | "replace": "((হ|ঃ|(হ্‌?))(্?)ল)", 431 | "rules": [] 432 | }, 433 | { 434 | "find": "hm", 435 | "replace": "((হ|ঃ|(হ্‌?))(্?)ম)", 436 | "rules": [] 437 | }, 438 | { 439 | "find": "hn", 440 | "replace": "((হ|ঃ|(হ্‌?))(্?)[নণঁঙঞং])", 441 | "rules": [] 442 | }, 443 | { 444 | "find": "ia", 445 | "replace": "((ঞা)|(([ইঈিী]|(য়[িী]))(([অএ]্যা?)|[আএ]|([‍‌]?(্য)?া)|(য়া))))", 446 | "rules": [] 447 | }, 448 | { 449 | "find": "jh", 450 | "replace": "(ঝ|(([জয]|(জ়))(্?)(হ|ঃ|(হ্‌?))))", 451 | "rules": [] 452 | }, 453 | { 454 | "find": "jj", 455 | "replace": "((হ্য)|(([জয]|(জ়))(্?)(([জয]|(জ়)))?))", 456 | "rules": [] 457 | }, 458 | { 459 | "find": "kh", 460 | "replace": "(খ|(ক্ষ)|(ক(্?)(হ|ঃ|(হ্‌?))))", 461 | "rules": [] 462 | }, 463 | { 464 | "find": "kk", 465 | "replace": "(ক(্?)ক?)", 466 | "rules": [] 467 | }, 468 | { 469 | "find": "ks", 470 | "replace": "(ক(্?)[সশষ])", 471 | "rules": [] 472 | }, 473 | { 474 | "find": "kx", 475 | "replace": "((ক্ষ)|(ক(্?)((ক্স)|(এক্স)|ষ)))", 476 | "rules": [] 477 | }, 478 | { 479 | "find": "lb", 480 | "replace": "(ল(্?)ব)", 481 | "rules": [] 482 | }, 483 | { 484 | "find": "ld", 485 | "replace": "(ল(্?)[দড])", 486 | "rules": [] 487 | }, 488 | { 489 | "find": "lg", 490 | "replace": "(ল(্?)(গ|(জ্ঞ)))", 491 | "rules": [] 492 | }, 493 | { 494 | "find": "lk", 495 | "replace": "(ল(্?)ক)", 496 | "rules": [] 497 | }, 498 | { 499 | "find": "ll", 500 | "replace": "((হ্ল)|(ল?(্?)ল)|(ল(্?)ল))", 501 | "rules": [] 502 | }, 503 | { 504 | "find": "lp", 505 | "replace": "(ল(্?)প)", 506 | "rules": [] 507 | }, 508 | { 509 | "find": "mb", 510 | "replace": "(ম(্?)ব)", 511 | "rules": [] 512 | }, 513 | { 514 | "find": "mm", 515 | "replace": "((হ্ম)|(ম(্?)ম?))", 516 | "rules": [] 517 | }, 518 | { 519 | "find": "mp", 520 | "replace": "(ম(্?)প)", 521 | "rules": [] 522 | }, 523 | { 524 | "find": "mt", 525 | "replace": "(ম(্?)[তটৎ])", 526 | "rules": [] 527 | }, 528 | { 529 | "find": "nc", 530 | "replace": "((ঞ্চ)|([নণঁঙঞং](্?)[চছ]))", 531 | "rules": [] 532 | }, 533 | { 534 | "find": "nd", 535 | "replace": "([নণঁঙঞং](্?)[দড])", 536 | "rules": [] 537 | }, 538 | { 539 | "find": "ng", 540 | "replace": "(ঙ|ং|ঞ|(ঙ্গ)|([নণঁঙঞং](্?)(গ|(জ্ঞ))))", 541 | "rules": [] 542 | }, 543 | { 544 | "find": "nj", 545 | "replace": "((ঞ্জ)|([নণঁঙঞং](্?)([জয]|(জ়))))", 546 | "rules": [] 547 | }, 548 | { 549 | "find": "nk", 550 | "replace": "((ঙ্ক)|([নণঁঙঞং](্?)ক))", 551 | "rules": [] 552 | }, 553 | { 554 | "find": "nn", 555 | "replace": "((হ্ণ)|(হ্ন)|([নণঁঙঞং](্?)[নণঁঙঞং]?))", 556 | "rules": [] 557 | }, 558 | { 559 | "find": "nt", 560 | "replace": "([নণঁঙঞং](্?)[তটৎ])", 561 | "rules": [] 562 | }, 563 | { 564 | "find": "oi", 565 | "replace": "(ঐ|ৈ|(([ওোঅ]|(অ্য)|(য়ো?))?([ইঈিী]|(য়[িী]))))", 566 | "rules": [] 567 | }, 568 | { 569 | "find": "oo", 570 | "replace": "((([উঊুূ]|(য়[ুূ])))|(([ওোঅ]|(অ্য)|(য়ো?))?([ওোঅ]|(অ্য)|(য়ো?))?))", 571 | "rules": [] 572 | }, 573 | { 574 | "find": "ou", 575 | "replace": "(ঔ|ৌ|(([ওোঅ]|(অ্য)|(য়ো?))?([উঊুূ]|(য়[ুূ]))))", 576 | "rules": [] 577 | }, 578 | { 579 | "find": "ph", 580 | "replace": "(ফ|(প(্?)(হ|ঃ|(হ্‌?))))", 581 | "rules": [] 582 | }, 583 | { 584 | "find": "pp", 585 | "replace": "(প(্?)প?)", 586 | "rules": [] 587 | }, 588 | { 589 | "find": "qq", 590 | "replace": "(ক(্?)ক?)", 591 | "rules": [] 592 | }, 593 | { 594 | "find": "rh", 595 | "replace": "((([রড়ঢ়]|(হ্র)))|(([রড়ঢ়]|(হ্র))(্?)(হ|ঃ|(হ্‌?))))", 596 | "rules": [] 597 | }, 598 | { 599 | "find": "ri", 600 | "replace": "(ঋ|ৃ|(হৃ)|(([রড়ঢ়]|(হ্র))([ইঈিী]|(য়[িী]))))", 601 | "rules": [] 602 | }, 603 | { 604 | "find": "sh", 605 | "replace": "(স|শ|ষ|([সশষ](্?)(হ|ঃ|(হ্‌?))))", 606 | "rules": [] 607 | }, 608 | { 609 | "find": "ss", 610 | "replace": "([সশষ](্?)[সশষ]?)", 611 | "rules": [] 612 | }, 613 | { 614 | "find": "th", 615 | "replace": "(থ|ঠ|([তটৎ](্?)(হ|ঃ|(হ্‌?))))", 616 | "rules": [] 617 | }, 618 | { 619 | "find": "tt", 620 | "replace": "([তটৎ](্?)[তটৎ]?)", 621 | "rules": [] 622 | }, 623 | { 624 | "find": "uu", 625 | "replace": "(ঊ|ূ|(([উঊুূ]|(য়[ুূ]))(([উঊুূ]|(য়[ুূ])))?))", 626 | "rules": [] 627 | }, 628 | { 629 | "find": "vv", 630 | "replace": "(ভ(্?)ভ?)", 631 | "rules": [] 632 | }, 633 | { 634 | "find": "xm", 635 | "replace": "(((ক্স)|(এক্স)|ষ)(্?)ম)", 636 | "rules": [] 637 | }, 638 | { 639 | "find": "xn", 640 | "replace": "(((ক্স)|(এক্স)|ষ)(্?)[নণঁঙঞং])", 641 | "rules": [] 642 | }, 643 | { 644 | "find": "zh", 645 | "replace": "(ঝ|(([জয]|(জ়))(্?)(হ|ঃ|(হ্‌?))))", 646 | "rules": [] 647 | }, 648 | { 649 | "find": "zz", 650 | "replace": "((হ্য)|((জ|য|(জ়)|([‍‌]?্য))(্?)((জ|য|(জ়)|([‍‌]?্য)))?))", 651 | "rules": [] 652 | }, 653 | { 654 | "find": "0", 655 | "replace": "(০|(0)|(শূন্য))", 656 | "rules": [] 657 | }, 658 | { 659 | "find": "1", 660 | "replace": "(১|(1)|(এক))", 661 | "rules": [] 662 | }, 663 | { 664 | "find": "2", 665 | "replace": "(২|(2)|(দুই))", 666 | "rules": [] 667 | }, 668 | { 669 | "find": "3", 670 | "replace": "(৩|(3)|(তিন))", 671 | "rules": [] 672 | }, 673 | { 674 | "find": "4", 675 | "replace": "(৪|(4)|(চার))", 676 | "rules": [] 677 | }, 678 | { 679 | "find": "5", 680 | "replace": "(৫|(5)|(পাঁচ))", 681 | "rules": [] 682 | }, 683 | { 684 | "find": "6", 685 | "replace": "((6)|৬|(ছয়))", 686 | "rules": [] 687 | }, 688 | { 689 | "find": "7", 690 | "replace": "(৭|(7)|(সাত))", 691 | "rules": [] 692 | }, 693 | { 694 | "find": "8", 695 | "replace": "(৮|(8)|(আট))", 696 | "rules": [] 697 | }, 698 | { 699 | "find": "9", 700 | "replace": "(৯|(9)|(নয়))", 701 | "rules": [] 702 | }, 703 | { 704 | "find": "`", 705 | "replace": "", 706 | "rules": [] 707 | }, 708 | { 709 | "find": "a", 710 | "replace": "(([অএ]্যা?)|[অআএ]|([‍‌]?(্য)?া)|(য়া))", 711 | "rules": [] 712 | }, 713 | { 714 | "find": "b", 715 | "replace": "ব", 716 | "rules": [] 717 | }, 718 | { 719 | "find": "c", 720 | "replace": "[চছ]", 721 | "rules": [] 722 | }, 723 | { 724 | "find": "d", 725 | "replace": "[দড]", 726 | "rules": [] 727 | }, 728 | { 729 | "find": "e", 730 | "replace": "((এ্যা?)|[এে]|([‍‌]?(্য)া)|(য়ে))", 731 | "rules": [] 732 | }, 733 | { 734 | "find": "f", 735 | "replace": "ফ", 736 | "rules": [] 737 | }, 738 | { 739 | "find": "g", 740 | "replace": "(গ|(জ্ঞ))", 741 | "rules": [] 742 | }, 743 | { 744 | "find": "h", 745 | "replace": "(হ|ঃ|(হ্‌?))", 746 | "rules": [] 747 | }, 748 | { 749 | "find": "i", 750 | "replace": "([ইঈিী]|(য়[িী]))", 751 | "rules": [] 752 | }, 753 | { 754 | "find": "j", 755 | "replace": "([জয]|(জ়))", 756 | "rules": [] 757 | }, 758 | { 759 | "find": "k", 760 | "replace": "ক", 761 | "rules": [] 762 | }, 763 | { 764 | "find": "l", 765 | "replace": "ল", 766 | "rules": [] 767 | }, 768 | { 769 | "find": "m", 770 | "replace": "ম", 771 | "rules": [] 772 | }, 773 | { 774 | "find": "n", 775 | "replace": "[নণঁঙঞং]", 776 | "rules": [] 777 | }, 778 | { 779 | "find": "o", 780 | "replace": "([ওোঅ]|(অ্য)|(য়ো?))?", 781 | "rules": [ 782 | { 783 | "matches": [ 784 | { 785 | "negative": "FALSE", 786 | "scope": "punctuation", 787 | "type": "prefix", 788 | "value": "" 789 | } 790 | ], 791 | "replace": "([ওোঅ]|(অ্য)|(য়ো?))" 792 | } 793 | ] 794 | }, 795 | { 796 | "find": "p", 797 | "replace": "প", 798 | "rules": [] 799 | }, 800 | { 801 | "find": "q", 802 | "replace": "ক", 803 | "rules": [] 804 | }, 805 | { 806 | "find": "r", 807 | "replace": "([রড়ঢ়]|(হ্র))", 808 | "rules": [] 809 | }, 810 | { 811 | "find": "s", 812 | "replace": "[সশষ]", 813 | "rules": [] 814 | }, 815 | { 816 | "find": "t", 817 | "replace": "[তটৎ]", 818 | "rules": [] 819 | }, 820 | { 821 | "find": "u", 822 | "replace": "([উঊুূ]|(য়[ুূ]))", 823 | "rules": [] 824 | }, 825 | { 826 | "find": "v", 827 | "replace": "ভ", 828 | "rules": [] 829 | }, 830 | { 831 | "find": "w", 832 | "replace": "(ও|(ওয়)|(্ব))", 833 | "rules": [] 834 | }, 835 | { 836 | "find": "x", 837 | "replace": "((ক্স)|(এক্স)|ষ)", 838 | "rules": [] 839 | }, 840 | { 841 | "find": "y", 842 | "replace": "(য়|(ইয়)|([‍‌]?্য))", 843 | "rules": [] 844 | }, 845 | { 846 | "find": "z", 847 | "replace": "(জ|য|(জ়)|([‍‌]?্য))", 848 | "rules": [] 849 | } 850 | ], 851 | "vowel": "aeiou", 852 | "consonant": "bcdfghjklmnpqrstvwxyz", 853 | "casesensitive": "|()[]{}^$*+?.~!@#%&-_='\";<>/\\,:`" 854 | } --------------------------------------------------------------------------------