| 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 | 
|---|
| 12 | Classes, | 
|---|
| 13 | SysUtils; | 
|---|
| 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 | 
|---|
| 52 | ACLUtility, | 
|---|
| 53 | ACLLanguageUnit, | 
|---|
| 54 | CharUtilsUnit, | 
|---|
| 55 | StringUtilsUnit, | 
|---|
| 56 | DebugUnit; | 
|---|
| 57 |  | 
|---|
| 58 | var | 
|---|
| 59 | QueryErrorMissingWord1: string; | 
|---|
| 60 | QueryErrorMissingWord2: string; | 
|---|
| 61 |  | 
|---|
| 62 | Procedure OnLanguageEvent( Language: TLanguageFile; | 
|---|
| 63 | const Apply: boolean ); | 
|---|
| 64 | begin | 
|---|
| 65 |  | 
|---|
| 66 | Language.Prefix := 'SearchQuery.'; | 
|---|
| 67 | Language.LL( Apply, QueryErrorMissingWord1, 'QueryErrorMissingWord1', 'No search word given after ' ); | 
|---|
| 68 | Language.LL( Apply, QueryErrorMissingWord2, 'QueryErrorMissingWord2', ' before ' ); | 
|---|
| 69 | end; | 
|---|
| 70 |  | 
|---|
| 71 | constructor TTextSearchQuery.Create( const SearchString: string ); | 
|---|
| 72 | var | 
|---|
| 73 | TermText: string; | 
|---|
| 74 | CombineMethod: TSearchTermCombineMethod; | 
|---|
| 75 | Term: TSearchTerm; | 
|---|
| 76 | tmpTerms : TStringList; | 
|---|
| 77 | i : integer; | 
|---|
| 78 | begin | 
|---|
| 79 | Terms := TList.Create; | 
|---|
| 80 | try | 
|---|
| 81 | tmpTerms := TStringList.Create; | 
|---|
| 82 | StrExtractStringsQuoted(tmpTerms, SearchString); | 
|---|
| 83 |  | 
|---|
| 84 | for i := 0 to tmpTerms.count-1 do | 
|---|
| 85 | begin | 
|---|
| 86 | TermText := tmpTerms[i]; | 
|---|
| 87 |  | 
|---|
| 88 | // Check for modifiers: | 
|---|
| 89 | //  + word must be matched | 
|---|
| 90 | //  - word must not be matched | 
|---|
| 91 | case TermText[ 1 ] of | 
|---|
| 92 | '+': | 
|---|
| 93 | CombineMethod := cmRequired; | 
|---|
| 94 | '-': | 
|---|
| 95 | CombineMethod := cmExcluded; | 
|---|
| 96 | else | 
|---|
| 97 | CombineMethod := cmOptional; | 
|---|
| 98 | end; | 
|---|
| 99 |  | 
|---|
| 100 | if CombineMethod <> cmOptional then | 
|---|
| 101 | begin | 
|---|
| 102 | // delete + or - | 
|---|
| 103 | if Length( TermText ) = 1 then | 
|---|
| 104 | if (i < tmpTerms.count-1) then | 
|---|
| 105 | raise ESearchSyntaxError.Create( QueryErrorMissingWord1 | 
|---|
| 106 | + StrInDoubleQuotes(TermText) | 
|---|
| 107 | + QueryErrorMissingWord2 | 
|---|
| 108 | + StrInDoubleQuotes(tmpTerms[i+1]) ) | 
|---|
| 109 | else | 
|---|
| 110 | raise ESearchSyntaxError.Create( QueryErrorMissingWord1 | 
|---|
| 111 | + StrInDoubleQuotes(TermText)); | 
|---|
| 112 | Delete( TermText, 1, 1 ); | 
|---|
| 113 | end; | 
|---|
| 114 |  | 
|---|
| 115 | Term := TSearchTerm.Create( TermText, | 
|---|
| 116 | CombineMethod ); | 
|---|
| 117 | Terms.Add( Term ); | 
|---|
| 118 | end; | 
|---|
| 119 | tmpTerms.Destroy; | 
|---|
| 120 | except | 
|---|
| 121 | tmpTerms.Destroy; | 
|---|
| 122 | Destroy; // clean up | 
|---|
| 123 | raise; // reraise exception | 
|---|
| 124 | end; | 
|---|
| 125 | end; | 
|---|
| 126 |  | 
|---|
| 127 | destructor TTextSearchQuery.Destroy; | 
|---|
| 128 | begin | 
|---|
| 129 | DestroyListObjects( Terms ); | 
|---|
| 130 | Terms.Destroy; | 
|---|
| 131 | end; | 
|---|
| 132 |  | 
|---|
| 133 | function TTextSearchQuery.GetTerm( index: longint ): TSearchTerm; | 
|---|
| 134 | begin | 
|---|
| 135 | Result := Terms[ Index ]; | 
|---|
| 136 | end; | 
|---|
| 137 |  | 
|---|
| 138 | function TTextSearchQuery.GetTermCount: longint; | 
|---|
| 139 | begin | 
|---|
| 140 | Result := Terms.Count; | 
|---|
| 141 | end; | 
|---|
| 142 |  | 
|---|
| 143 | constructor TSearchTerm.Create( const TheText: string; | 
|---|
| 144 | const TheCombineMethod: TSearchTermCombineMethod ); | 
|---|
| 145 | var | 
|---|
| 146 | TermParseIndex: longint; | 
|---|
| 147 | TermChar: char; | 
|---|
| 148 | TermPart: string; | 
|---|
| 149 | begin | 
|---|
| 150 | Parts := TStringList.Create; | 
|---|
| 151 |  | 
|---|
| 152 | Text := TheText; | 
|---|
| 153 | CombineMethod := TheCombineMethod; | 
|---|
| 154 |  | 
|---|
| 155 | // Break out each part of the term as IPF does: | 
|---|
| 156 | // consecutive alphanumeric chars become a "word" | 
|---|
| 157 | // but each symbol is a separate word, and symbols break | 
|---|
| 158 | // up alphanumerics into multiple words. e.g. | 
|---|
| 159 | // CAKE_SAUSAGE becomes three words in IPF, | 
|---|
| 160 | // one each for "CAKE" "_" and "SAUSAGE" | 
|---|
| 161 |  | 
|---|
| 162 | TermParseIndex := 1; | 
|---|
| 163 | while TermParseIndex <= Length( Text ) do | 
|---|
| 164 | begin | 
|---|
| 165 | // collect alphanumeric chars | 
|---|
| 166 | TermPart := ''; | 
|---|
| 167 | while TermParseIndex <= Length( Text ) do | 
|---|
| 168 | begin | 
|---|
| 169 | TermChar := Text[ TermParseIndex ]; | 
|---|
| 170 | if  (    CharIsAlpha( TermChar ) | 
|---|
| 171 | or CharIsDigit( TermChar ) ) then | 
|---|
| 172 | begin | 
|---|
| 173 | // alpha numeric, collect it | 
|---|
| 174 | TermPart := TermPart + TermChar; | 
|---|
| 175 | inc( TermParseIndex ); | 
|---|
| 176 | end | 
|---|
| 177 | else | 
|---|
| 178 | begin | 
|---|
| 179 | // not alpha numeric, so stop | 
|---|
| 180 | break; | 
|---|
| 181 | end; | 
|---|
| 182 | end; | 
|---|
| 183 | if Length( TermPart ) > 0 then | 
|---|
| 184 | begin | 
|---|
| 185 | Parts.Add( TermPart ); // add collected alphanumeric part | 
|---|
| 186 | end; | 
|---|
| 187 |  | 
|---|
| 188 | if TermParseIndex <= Length( Text ) then | 
|---|
| 189 | begin | 
|---|
| 190 | // must be a symbol, | 
|---|
| 191 | // each symbol (excluding space) is an individual item | 
|---|
| 192 | if Text[ TermParseIndex ] <> ' ' then | 
|---|
| 193 | Parts.Add( Text[ TermParseIndex ] ); | 
|---|
| 194 | inc( TermParseIndex ); | 
|---|
| 195 | end; | 
|---|
| 196 |  | 
|---|
| 197 | end; | 
|---|
| 198 |  | 
|---|
| 199 | end; | 
|---|
| 200 |  | 
|---|
| 201 | destructor TSearchTerm.Destroy; | 
|---|
| 202 | begin | 
|---|
| 203 | Parts.Destroy; | 
|---|
| 204 | end; | 
|---|
| 205 |  | 
|---|
| 206 | Initialization | 
|---|
| 207 | RegisterProcForLanguages( OnLanguageEvent ); | 
|---|
| 208 | End. | 
|---|