| [18] | 1 | Unit TextSearchQuery; | 
|---|
|  | 2 |  | 
|---|
|  | 3 | // NewView - a new OS/2 Help Viewer | 
|---|
|  | 4 | // Copyright 2003 Aaron Lawrence (aaronl at consultant dot com) | 
|---|
|  | 5 | // This software is released under the Gnu Public License - see readme.txt | 
|---|
|  | 6 |  | 
|---|
|  | 7 | Interface | 
|---|
|  | 8 |  | 
|---|
|  | 9 | // Encapsulates a parsed search query. | 
|---|
|  | 10 |  | 
|---|
|  | 11 | uses | 
|---|
| [33] | 12 | Classes, | 
|---|
|  | 13 | SysUtils; | 
|---|
| [18] | 14 |  | 
|---|
|  | 15 | Type | 
|---|
|  | 16 | ESearchSyntaxError = class( Exception ) | 
|---|
|  | 17 | end; | 
|---|
|  | 18 |  | 
|---|
|  | 19 | TSearchTermCombineMethod = | 
|---|
|  | 20 | ( | 
|---|
|  | 21 | cmOptional, | 
|---|
|  | 22 | cmRequired, | 
|---|
|  | 23 | cmExcluded | 
|---|
|  | 24 | ); | 
|---|
|  | 25 |  | 
|---|
|  | 26 | TSearchTerm = class | 
|---|
|  | 27 | Text: string; | 
|---|
|  | 28 | Parts: TStringList; | 
|---|
|  | 29 | CombineMethod: TSearchTermCombineMethod; | 
|---|
|  | 30 |  | 
|---|
|  | 31 | constructor Create( const TheText: string; | 
|---|
|  | 32 | const TheCombineMethod: TSearchTermCombineMethod ); | 
|---|
|  | 33 | destructor Destroy; override; | 
|---|
|  | 34 | end; | 
|---|
|  | 35 |  | 
|---|
|  | 36 | TTextSearchQuery = class | 
|---|
|  | 37 | protected | 
|---|
|  | 38 | Terms: TList; | 
|---|
|  | 39 | function GetTerm( Index: longint ): TSearchTerm; | 
|---|
|  | 40 | function GetTermCount: longint; | 
|---|
|  | 41 | public | 
|---|
|  | 42 | constructor Create( const SearchString: string ); | 
|---|
|  | 43 | destructor Destroy; override; | 
|---|
|  | 44 |  | 
|---|
|  | 45 | property Term[ Index: longint ]: TSearchTerm read GetTerm; | 
|---|
|  | 46 | property TermCount: longint read GetTermCount; | 
|---|
|  | 47 | end; | 
|---|
|  | 48 |  | 
|---|
|  | 49 | Implementation | 
|---|
|  | 50 |  | 
|---|
|  | 51 | uses | 
|---|
| [33] | 52 | ACLStringUtility, | 
|---|
|  | 53 | ACLUtility, | 
|---|
|  | 54 | ACLLanguageUnit; | 
|---|
| [18] | 55 |  | 
|---|
|  | 56 | var | 
|---|
|  | 57 | QueryErrorMissingWord1: string; | 
|---|
|  | 58 | QueryErrorMissingWord2: string; | 
|---|
|  | 59 |  | 
|---|
|  | 60 | Procedure OnLanguageEvent( Language: TLanguageFile; | 
|---|
|  | 61 | const Apply: boolean ); | 
|---|
|  | 62 | begin | 
|---|
|  | 63 |  | 
|---|
|  | 64 | Language.Prefix := 'SearchQuery.'; | 
|---|
|  | 65 | Language.LL( Apply, QueryErrorMissingWord1, 'QueryErrorMissingWord1', 'No search word given after ' ); | 
|---|
|  | 66 | Language.LL( Apply, QueryErrorMissingWord2, 'QueryErrorMissingWord2', ' before ' ); | 
|---|
|  | 67 | end; | 
|---|
|  | 68 |  | 
|---|
|  | 69 | constructor TTextSearchQuery.Create( const SearchString: string ); | 
|---|
|  | 70 | var | 
|---|
|  | 71 | TermText: string; | 
|---|
|  | 72 | CombineMethod: TSearchTermCombineMethod; | 
|---|
|  | 73 | Term: TSearchTerm; | 
|---|
|  | 74 | ParseIndex: longint; | 
|---|
|  | 75 | begin | 
|---|
|  | 76 | Terms := TList.Create; | 
|---|
|  | 77 | try | 
|---|
|  | 78 | ParseIndex := 1; | 
|---|
|  | 79 | while ParseIndex <= Length( SearchString ) do | 
|---|
|  | 80 | begin | 
|---|
|  | 81 | GetNextQuotedValue( SearchString, | 
|---|
|  | 82 | ParseIndex, | 
|---|
|  | 83 | TermText, | 
|---|
|  | 84 | DoubleQuote ); | 
|---|
|  | 85 |  | 
|---|
|  | 86 | // Check for modifiers: | 
|---|
|  | 87 | //  + word must be matched | 
|---|
|  | 88 | //  - word must not be matched | 
|---|
|  | 89 | case TermText[ 1 ] of | 
|---|
|  | 90 | '+': | 
|---|
|  | 91 | CombineMethod := cmRequired; | 
|---|
|  | 92 | '-': | 
|---|
|  | 93 | CombineMethod := cmExcluded; | 
|---|
|  | 94 | else | 
|---|
|  | 95 | CombineMethod := cmOptional; | 
|---|
|  | 96 | end; | 
|---|
|  | 97 |  | 
|---|
|  | 98 | if CombineMethod <> cmOptional then | 
|---|
|  | 99 | begin | 
|---|
|  | 100 | // delete + or - | 
|---|
|  | 101 | if Length( TermText ) = 1 then | 
|---|
|  | 102 | raise ESearchSyntaxError.Create( QueryErrorMissingWord1 | 
|---|
|  | 103 | + StrDoubleQuote( TermText ) | 
|---|
|  | 104 | + QueryErrorMissingWord2 | 
|---|
|  | 105 | + StrDoubleQuote( StrRightFrom( SearchString, | 
|---|
|  | 106 | ParseIndex ) ) ); | 
|---|
|  | 107 | Delete( TermText, 1, 1 ); | 
|---|
|  | 108 | end; | 
|---|
|  | 109 |  | 
|---|
|  | 110 | Term := TSearchTerm.Create( TermText, | 
|---|
|  | 111 | CombineMethod ); | 
|---|
|  | 112 | Terms.Add( Term ); | 
|---|
|  | 113 | end; | 
|---|
|  | 114 | except | 
|---|
|  | 115 | Destroy; // clean up | 
|---|
|  | 116 | raise; // reraise exception | 
|---|
|  | 117 | end; | 
|---|
|  | 118 | end; | 
|---|
|  | 119 |  | 
|---|
|  | 120 | destructor TTextSearchQuery.Destroy; | 
|---|
|  | 121 | begin | 
|---|
|  | 122 | DestroyListObjects( Terms ); | 
|---|
|  | 123 | Terms.Destroy; | 
|---|
|  | 124 | end; | 
|---|
|  | 125 |  | 
|---|
|  | 126 | function TTextSearchQuery.GetTerm( index: longint ): TSearchTerm; | 
|---|
|  | 127 | begin | 
|---|
|  | 128 | Result := Terms[ Index ]; | 
|---|
|  | 129 | end; | 
|---|
|  | 130 |  | 
|---|
|  | 131 | function TTextSearchQuery.GetTermCount: longint; | 
|---|
|  | 132 | begin | 
|---|
|  | 133 | Result := Terms.Count; | 
|---|
|  | 134 | end; | 
|---|
|  | 135 |  | 
|---|
|  | 136 | constructor TSearchTerm.Create( const TheText: string; | 
|---|
|  | 137 | const TheCombineMethod: TSearchTermCombineMethod ); | 
|---|
|  | 138 | var | 
|---|
|  | 139 | TermParseIndex: longint; | 
|---|
|  | 140 | TermChar: char; | 
|---|
|  | 141 | TermPart: string; | 
|---|
|  | 142 | begin | 
|---|
|  | 143 | Parts := TStringList.Create; | 
|---|
|  | 144 |  | 
|---|
|  | 145 | Text := TheText; | 
|---|
|  | 146 | CombineMethod := TheCombineMethod; | 
|---|
|  | 147 |  | 
|---|
|  | 148 | // Break out each part of the term as IPF does: | 
|---|
|  | 149 | // consecutive alphanumeric chars become a "word" | 
|---|
|  | 150 | // but each symbol is a separate word, and symbols break | 
|---|
|  | 151 | // up alphanumerics into multiple words. e.g. | 
|---|
|  | 152 | // CAKE_SAUSAGE becomes three words in IPF, | 
|---|
|  | 153 | // one each for "CAKE" "_" and "SAUSAGE" | 
|---|
|  | 154 |  | 
|---|
|  | 155 | TermParseIndex := 1; | 
|---|
|  | 156 | while TermParseIndex <= Length( Text ) do | 
|---|
|  | 157 | begin | 
|---|
|  | 158 | // collect alphanumeric chars | 
|---|
|  | 159 | TermPart := ''; | 
|---|
|  | 160 | while TermParseIndex <= Length( Text ) do | 
|---|
|  | 161 | begin | 
|---|
|  | 162 | TermChar := Text[ TermParseIndex ]; | 
|---|
|  | 163 | if  (    IsAlpha( TermChar ) | 
|---|
|  | 164 | or IsDigit( TermChar ) ) then | 
|---|
|  | 165 | begin | 
|---|
|  | 166 | // alpha numeric, collect it | 
|---|
|  | 167 | TermPart := TermPart + TermChar; | 
|---|
|  | 168 | inc( TermParseIndex ); | 
|---|
|  | 169 | end | 
|---|
|  | 170 | else | 
|---|
|  | 171 | begin | 
|---|
|  | 172 | // not alpha numeric, so stop | 
|---|
|  | 173 | break; | 
|---|
|  | 174 | end; | 
|---|
|  | 175 | end; | 
|---|
|  | 176 | if Length( TermPart ) > 0 then | 
|---|
|  | 177 | begin | 
|---|
|  | 178 | Parts.Add( TermPart ); // add collected alphanumeric part | 
|---|
|  | 179 | end; | 
|---|
|  | 180 |  | 
|---|
|  | 181 | if TermParseIndex <= Length( Text ) then | 
|---|
|  | 182 | begin | 
|---|
|  | 183 | // must be a symbol, | 
|---|
|  | 184 | // each symbol (excluding space) is an individual item | 
|---|
|  | 185 | if Text[ TermParseIndex ] <> ' ' then | 
|---|
|  | 186 | Parts.Add( Text[ TermParseIndex ] ); | 
|---|
|  | 187 | inc( TermParseIndex ); | 
|---|
|  | 188 | end; | 
|---|
|  | 189 |  | 
|---|
|  | 190 | end; | 
|---|
|  | 191 |  | 
|---|
|  | 192 | end; | 
|---|
|  | 193 |  | 
|---|
|  | 194 | destructor TSearchTerm.Destroy; | 
|---|
|  | 195 | begin | 
|---|
|  | 196 | Parts.Destroy; | 
|---|
|  | 197 | end; | 
|---|
|  | 198 |  | 
|---|
|  | 199 | Initialization | 
|---|
|  | 200 | RegisterProcForLanguages( OnLanguageEvent ); | 
|---|
|  | 201 | End. | 
|---|