| 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 | ACLStringUtility, | 
|---|
| 53 | ACLUtility, | 
|---|
| 54 | ACLLanguageUnit; | 
|---|
| 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. | 
|---|