| 1 | unit uParam;
 | 
|---|
| 2 | 
 | 
|---|
| 3 | interface
 | 
|---|
| 4 | 
 | 
|---|
| 5 | uses
 | 
|---|
| 6 |   Classes;
 | 
|---|
| 7 | 
 | 
|---|
| 8 | Type
 | 
|---|
| 9 |   TParamType = (Literal, Reference, List, Undefined);
 | 
|---|
| 10 | 
 | 
|---|
| 11 |   TParamMult = class
 | 
|---|
| 12 |   private
 | 
|---|
| 13 |     mName: String;
 | 
|---|
| 14 |     mMultiple: NameValueCollection;
 | 
|---|
| 15 |   protected
 | 
|---|
| 16 |     function GetCount: Integer;
 | 
|---|
| 17 |     function GetFirst: String;
 | 
|---|
| 18 |     function GetLast: String;
 | 
|---|
| 19 |     function GetMultiple(index: String): string;
 | 
|---|
| 20 |     procedure SetMultiple(index, value: string);
 | 
|---|
| 21 |   public
 | 
|---|
| 22 |     property Name: String read mName write mName;
 | 
|---|
| 23 |     property Count: Integer read GetCount;
 | 
|---|
| 24 |     property First: String read GetFirst;
 | 
|---|
| 25 |     property Last: String read GetLast;
 | 
|---|
| 26 |     property Self[index: string] read GetMultiple write SetMultiple;
 | 
|---|
| 27 |   end;
 | 
|---|
| 28 | 
 | 
|---|
| 29 |   TParamRecord = class
 | 
|---|
| 30 |   private
 | 
|---|
| 31 |     mValue: String;
 | 
|---|
| 32 |     mPType: TParamType;
 | 
|---|
| 33 |     mMult: TParamMult;
 | 
|---|
| 34 |   public
 | 
|---|
| 35 |     property Value: String read mValue write mValue;
 | 
|---|
| 36 |     property PType: TParamType read mPType write mPType;
 | 
|---|
| 37 |     property Mult: TParamMult read mMult write mMult;
 | 
|---|
| 38 |   end;
 | 
|---|
| 39 | 
 | 
|---|
| 40 |   TParam = class
 | 
|---|
| 41 |   private
 | 
|---|
| 42 |     mParameters: TList;
 | 
|---|
| 43 |   protected
 | 
|---|
| 44 |     procedure SetParameter(index: Integer; parameter: TParamRecord);
 | 
|---|
| 45 |     function GetParameter(index: Integer): TParamRecord;
 | 
|---|
| 46 |   public
 | 
|---|
| 47 |     Constructor Create; overload;
 | 
|---|
| 48 |     Constructor Create(rpcParams: String); overload;
 | 
|---|
| 49 |     procedure Clear;
 | 
|---|
| 50 |     property ParamRecord[index: Integer]: TParam read GetParameter write SetParameter;
 | 
|---|
| 51 |     property Count: Integer read GetCount;
 | 
|---|
| 52 |   end;
 | 
|---|
| 53 | 
 | 
|---|
| 54 | 
 | 
|---|
| 55 | implementation
 | 
|---|
| 56 | 
 | 
|---|
| 57 |   // Thes classes are ported from Delphi and have hardly been tested.
 | 
|---|
| 58 |   // Use them at your own discression.
 | 
|---|
| 59 | 
 | 
|---|
| 60 |   /// <summary>
 | 
|---|
| 61 |   /// Summary description for Param.
 | 
|---|
| 62 |   /// </summary>
 | 
|---|
| 63 | procedure TParam.Clear;
 | 
|---|
| 64 | var
 | 
|---|
| 65 |   i: Integer;
 | 
|---|
| 66 | begin
 | 
|---|
| 67 |   for i:=0 to Pred(mParameters.Count) do
 | 
|---|
| 68 |         mParameters[i] := nil;
 | 
|---|
| 69 | 
 | 
|---|
| 70 |   mParameters.Clear;
 | 
|---|
| 71 | end;
 | 
|---|
| 72 | 
 | 
|---|
| 73 | procedure TParam.Assign(source: TParam)
 | 
|---|
| 74 | begin
 | 
|---|
| 75 |   Clear;
 | 
|---|
| 76 |   for i:=0 to Pred(source.Count) do
 | 
|---|
| 77 |   begin
 | 
|---|
| 78 |     Self[i].Value := source[i].Value;
 | 
|---|
| 79 |     Self[i].PType := source[i].PType;
 | 
|---|
| 80 |     Self[i].Mult.Assign(source[i].Mult);
 | 
|---|
| 81 |   end;
 | 
|---|
| 82 | end;
 | 
|---|
| 83 | 
 | 
|---|
| 84 | 
 | 
|---|
| 85 | Constructor TParam.Create;
 | 
|---|
| 86 | begin
 | 
|---|
| 87 |   mParameters := TList.Create;
 | 
|---|
| 88 | end;
 | 
|---|
| 89 | 
 | 
|---|
| 90 |     /// <summary>
 | 
|---|
| 91 |     /// This Param constructor takes a string and splits it into ParamRecords
 | 
|---|
| 92 |     /// </summary>
 | 
|---|
| 93 |     /// <param name:='rpcParams'></param>
 | 
|---|
| 94 | Constructor TParam.Create(rpcParams: String)
 | 
|---|
| 95 | var
 | 
|---|
| 96 |   i, curStart, lengthOfRpcParams, EndOfSegment: Integer;
 | 
|---|
| 97 |   aRef, aVal: String;
 | 
|---|
| 98 |   ParamRecord: TParamRecord;
 | 
|---|
| 99 | begin
 | 
|---|
| 100 | 
 | 
|---|
| 101 | const
 | 
|---|
| 102 |       // kSEP_FS: char = 28;
 | 
|---|
| 103 |   kSEP_GS: char = 29;
 | 
|---|
| 104 |   kSEP_US: char = 30;
 | 
|---|
| 105 |   kSEP_RS: char = 31;
 | 
|---|
| 106 | 
 | 
|---|
| 107 |   mParameters := TList.Create;
 | 
|---|
| 108 |   if (rpcParams <> nil) then
 | 
|---|
| 109 |   begin
 | 
|---|
| 110 |     curStart := 0;
 | 
|---|
| 111 |     i:= 0;
 | 
|---|
| 112 |     lengthOfrpcParams := Length(rpcParams);
 | 
|---|
| 113 |     while (curStart < lengthOfrpcParams-1)
 | 
|---|
| 114 |     begin
 | 
|---|
| 115 |       paramRecord := Self[i];
 | 
|---|
| 116 |       case (rpcParams[curStart]) of
 | 
|---|
| 117 |         'L' : paramRecord.PType := ParamType.Literal;
 | 
|---|
| 118 |         'R' : paramRecord.PType := ParamType.Reference;
 | 
|---|
| 119 |         'M' : paramRecord.PType := ParamType.List;
 | 
|---|
| 120 |         else : paramRecord.PType := ParamType.Undefined;
 | 
|---|
| 121 |       end;
 | 
|---|
| 122 |       curStart := CurStart + 2;
 | 
|---|
| 123 |       if (Self[i].PType = ParamType.List) then
 | 
|---|
| 124 |       begin
 | 
|---|
| 125 |         endOfSegment := 0;
 | 
|---|
| 126 |         while (rpcParams[curStart] <> kSEP_GS) do
 | 
|---|
| 127 |         begin
 | 
|---|
| 128 |           endOfSegment := PosNext(kSEP_US,curStart,lengthOfrpcParams,rpcParams);
 | 
|---|
| 129 |           aRef := rpcParams.Substring(curStart,endOfSegment - curStart);
 | 
|---|
| 130 |           curStart := endOfSegment + 1;
 | 
|---|
| 131 |           endOfSegment := PosNext(kSEP_RS,curStart,lengthOfrpcParams,rpcParams);
 | 
|---|
| 132 |           aVal := rpcParams.Substring(curStart,endOfSegment - curStart);
 | 
|---|
| 133 |           curStart := endOfSegment + 1;
 | 
|---|
| 134 |           Self[i].Mult[aRef] := aVal;
 | 
|---|
| 135 |         end;
 | 
|---|
| 136 |         if (endOfSegment = 0) then
 | 
|---|
| 137 |         begin
 | 
|---|
| 138 |           endOfSegment := PosNext(kSEP_GS,curStart,lengthOfrpcParams,rpcParams);
 | 
|---|
| 139 |           curStart := endOfSegment + 1;
 | 
|---|
| 140 |         end
 | 
|---|
| 141 |         else
 | 
|---|
| 142 |         begin
 | 
|---|
| 143 |             endOfSegment := PosNext(kSEP_GS,curStart,lengthOfrpcParams,rpcParams);
 | 
|---|
| 144 |             Self[i].Value := rpcParams.Substring(curStart,endOfSegment-curStart);
 | 
|---|
| 145 |             curStart := endOfSegment + 1;
 | 
|---|
| 146 |         end;
 | 
|---|
| 147 |         Inc(i);
 | 
|---|
| 148 |       end;
 | 
|---|
| 149 |   end
 | 
|---|
| 150 |   else
 | 
|---|
| 151 |   begin
 | 
|---|
| 152 |         // The user of this routine should always pass in a valid string
 | 
|---|
| 153 |         Assert(false);
 | 
|---|
| 154 |   end;
 | 
|---|
| 155 | end;
 | 
|---|
| 156 | 
 | 
|---|
| 157 | Destructor TParam.Destroy;
 | 
|---|
| 158 | begin
 | 
|---|
| 159 |   Clear;
 | 
|---|
| 160 |   mParameters.Free;
 | 
|---|
| 161 | end;
 | 
|---|
| 162 | 
 | 
|---|
| 163 |     // Private Methods
 | 
|---|
| 164 | function TParam.GetCount: Integer;
 | 
|---|
| 165 | begin
 | 
|---|
| 166 |   Result := mParameters.Count;
 | 
|---|
| 167 | end;
 | 
|---|
| 168 | 
 | 
|---|
| 169 | function TParam.GetParameter(int index): ParamRecord;
 | 
|---|
| 170 | begin
 | 
|---|
| 171 |   if (index >= mParameters.Count) then
 | 
|---|
| 172 |   begin
 | 
|---|
| 173 |     while (mParameters.Count <:= index) do // Setup placehoders
 | 
|---|
| 174 |        mParameters.Add(nil);
 | 
|---|
| 175 |   end;
 | 
|---|
| 176 |   if (mParameters[index] = nil)
 | 
|---|
| 177 |   begin
 | 
|---|
| 178 |     Result := TParamRecord.Create();
 | 
|---|
| 179 |     mParameters[index] := result;
 | 
|---|
| 180 |   end
 | 
|---|
| 181 |   else
 | 
|---|
| 182 |     Result := TParamRecord(mParameters[index]);
 | 
|---|
| 183 | end;
 | 
|---|
| 184 | 
 | 
|---|
| 185 | procedure TParam.SetParameter(index: Integer; parameter: ParamRecord);
 | 
|---|
| 186 | begin
 | 
|---|
| 187 |   if (index >= mParameters.Count) then
 | 
|---|
| 188 |     while (mParameters.Count <= index) do // Set up placeholders
 | 
|---|
| 189 |       mParameters.Add(nil);
 | 
|---|
| 190 | 
 | 
|---|
| 191 |    if (mParameters[index] = nil) then
 | 
|---|
| 192 |      mParameters[index] := parameter;
 | 
|---|
| 193 | end;
 | 
|---|
| 194 | 
 | 
|---|
| 195 | function TParam.PosNext(aChar: char; startPos, lengthOfRpcParams: Integer; rpcParams: String): Integer;
 | 
|---|
| 196 | begin
 | 
|---|
| 197 |   Assert(rpcParams <> nil);
 | 
|---|
| 198 | 
 | 
|---|
| 199 |   Result := 0;
 | 
|---|
| 200 |   while (result = 0) and (startPos < lengthOfRpcParams)  do
 | 
|---|
| 201 |   begin
 | 
|---|
| 202 |     if(rpcParams[startPos] = aChar)
 | 
|---|
| 203 |       Result := startPos;
 | 
|---|
| 204 |     Inc(startPos);
 | 
|---|
| 205 |   end;
 | 
|---|
| 206 | end;
 | 
|---|
| 207 | 
 | 
|---|
| 208 | 
 | 
|---|
| 209 | Constructor TParamRecord.Create;
 | 
|---|
| 210 | begin
 | 
|---|
| 211 |   mMult := new ParamMult();
 | 
|---|
| 212 |   mMult.Name := ParamMult.kMultNameDefault;
 | 
|---|
| 213 |   mValue := nil;
 | 
|---|
| 214 |   mPType := ParamType.Undefined;
 | 
|---|
| 215 | end;
 | 
|---|
| 216 | 
 | 
|---|
| 217 | Destructor TParamRecord.Destroy;
 | 
|---|
| 218 | begin
 | 
|---|
| 219 |       if(mMult <> nil)
 | 
|---|
| 220 |       begin
 | 
|---|
| 221 |         mMult := nil;
 | 
|---|
| 222 |       end;
 | 
|---|
| 223 |     end;
 | 
|---|
| 224 | 
 | 
|---|
| 225 |   // The ParamMult class uses a NameValueCollection dictionary/hashtable as opposted to a string list
 | 
|---|
| 226 |   // like in Delphi. I think I have ported this properly preserving the desired functionality
 | 
|---|
| 227 |   // I think the NameValueCollection is the right answer here, but there may be some
 | 
|---|
| 228 |   // nuances that have been missed.
 | 
|---|
| 229 |   // Also, an enumerator should be created (provide by NameValueCollection) if the
 | 
|---|
| 230 |   // list is to be iterated over a lot between changes (for read only access). This
 | 
|---|
| 231 |   // will provided enhanced performance.
 | 
|---|
| 232 | const kMultInstanceNameDefault: string := 'Mult_instance';
 | 
|---|
| 233 | const kMultNameDefault: string := 'Mult';
 | 
|---|
| 234 | 
 | 
|---|
| 235 |     // Public Methods
 | 
|---|
| 236 | Constructor TParamMult.Create;
 | 
|---|
| 237 | begin
 | 
|---|
| 238 |   mMultiple := TNameValueCollection.Create;
 | 
|---|
| 239 |   mName := '';
 | 
|---|
| 240 | end;
 | 
|---|
| 241 | 
 | 
|---|
| 242 | 
 | 
|---|
| 243 | Destructor TParamMult.Destroy;
 | 
|---|
| 244 | begin
 | 
|---|
| 245 |   ClearAll;
 | 
|---|
| 246 |   mMultiple.Free;
 | 
|---|
| 247 |   mMultiple := nil;
 | 
|---|
| 248 |   mName := nil;
 | 
|---|
| 249 | end;
 | 
|---|
| 250 |     
 | 
|---|
| 251 | function TParamMult.Position(subscript: string): Integer;
 | 
|---|
| 252 | var
 | 
|---|
| 253 |   i: Integer;
 | 
|---|
| 254 | begin
 | 
|---|
| 255 |   Result := -1;
 | 
|---|
| 256 | 
 | 
|---|
| 257 |   for i:=0 to Pred(mMultiple.Count) do
 | 
|---|
| 258 |   begin
 | 
|---|
| 259 |     if (mMultiple.GetKey(i) = subscript)
 | 
|---|
| 260 |     begin
 | 
|---|
| 261 |       Result := i;
 | 
|---|
| 262 |       break;
 | 
|---|
| 263 |     end;
 | 
|---|
| 264 |   end;
 | 
|---|
| 265 | end;
 | 
|---|
| 266 | 
 | 
|---|
| 267 | 
 | 
|---|
| 268 | function TParamMult.Subscript(position: Integer): String;
 | 
|---|
| 269 | begin
 | 
|---|
| 270 |   if(position > -1 && position < Count) then
 | 
|---|
| 271 |     result := mMultiple.GetKey(position);
 | 
|---|
| 272 | end;
 | 
|---|
| 273 |     /// <summary>
 | 
|---|
| 274 |     /// In Assign all of the items from source object are copied one by one into the
 | 
|---|
| 275 |     /// target.  So if the source is later destroyed, target object will continue
 | 
|---|
| 276 |     /// to hold the copy of all elements, completely unaffected.
 | 
|---|
| 277 |     /// The source can only be a NameValueCollection or else it with throw an exception.
 | 
|---|
| 278 |     /// </summary>
 | 
|---|
| 279 |     /// <param name:='source'></param>
 | 
|---|
| 280 | {
 | 
|---|
| 281 |     public void      Assign(object source)
 | 
|---|
| 282 |     begin
 | 
|---|
| 283 |       ClearAll();
 | 
|---|
| 284 |       if (source is ParamMult)
 | 
|---|
| 285 |         mMultiple.Add((NameValueCollection)source);
 | 
|---|
| 286 |       else
 | 
|---|
| 287 |         throw(new ParamMultException('Invalid source type'+ source.GetType()+' in method Assign'));
 | 
|---|
| 288 |     end;
 | 
|---|
| 289 | }
 | 
|---|
| 290 |     /// <summary>
 | 
|---|
| 291 |     /// Order returns the subscript string of the next or previous element from the
 | 
|---|
| 292 |     /// StartSubscript.  This is very similar to the $O function available in M.
 | 
|---|
| 293 |     /// nil string ('') is returned when reaching beyong the first or last
 | 
|---|
| 294 |     /// element, or when list is empty. Note: A major difference between the M $O 
 | 
|---|
| 295 |     /// and this function is that in this function StartSubscript must identify a valid subscript
 | 
|---|
| 296 |     /// in the list.
 | 
|---|
| 297 |     /// </summary>
 | 
|---|
| 298 |     /// <param name:='startSubscript'></param>
 | 
|---|
| 299 |     /// <param name:='direction'></param>
 | 
|---|
| 300 |     /// <returns></returns>
 | 
|---|
| 301 | function TParamMult.Order(startSubscript: String; direction: Integer): String;
 | 
|---|
| 302 | var
 | 
|---|
| 303 |   index: Integer;
 | 
|---|
| 304 | begin
 | 
|---|
| 305 |   if (startSubscript = '') then
 | 
|---|
| 306 |   begin
 | 
|---|
| 307 |     if(direction > 0) then
 | 
|---|
| 308 |       result := First
 | 
|---|
| 309 |     else
 | 
|---|
| 310 |       result := Last;
 | 
|---|
| 311 |   end
 | 
|---|
| 312 |   else
 | 
|---|
| 313 |   begin
 | 
|---|
| 314 |     index := Position(startSubscript);
 | 
|---|
| 315 |     if(index > -1) then
 | 
|---|
| 316 |     begin
 | 
|---|
| 317 |       if (index < (Count -1)) and (direction > 0) then
 | 
|---|
| 318 |         result := mMultiple[index+1]
 | 
|---|
| 319 |       else
 | 
|---|
| 320 |         result := mMultiple[index-1];
 | 
|---|
| 321 |     end;
 | 
|---|
| 322 |   end;
 | 
|---|
| 323 | end;
 | 
|---|
| 324 | 
 | 
|---|
| 325 | {
 | 
|---|
| 326 |     public class    ParamMultException : Exception
 | 
|---|
| 327 |     begin
 | 
|---|
| 328 |       public ParamMultException(string errorString)
 | 
|---|
| 329 |         :base(kParamMultExceptionLabel + errorString)beginend;
 | 
|---|
| 330 |       private const string kParamMultExceptionLabel := 'ParamMult Exeption: ';
 | 
|---|
| 331 |     end;
 | 
|---|
| 332 | }
 | 
|---|
| 333 | procedure TParamMult.ClearAll;
 | 
|---|
| 334 | begin
 | 
|---|
| 335 |   mMultiple.Clear();
 | 
|---|
| 336 | end;
 | 
|---|
| 337 | 
 | 
|---|
| 338 | 
 | 
|---|
| 339 | function TParamMult.GetCount: Integer;
 | 
|---|
| 340 | begin
 | 
|---|
| 341 |   Result := mMultiple.Count;
 | 
|---|
| 342 | end;
 | 
|---|
| 343 | 
 | 
|---|
| 344 | 
 | 
|---|
| 345 | function TParamMult.GetFirst: String;
 | 
|---|
| 346 | begin
 | 
|---|
| 347 |   Result := '';
 | 
|---|
| 348 |   if (mMultiple.Count > 0) then
 | 
|---|
| 349 |     Result := mMultiple[0];
 | 
|---|
| 350 | end;
 | 
|---|
| 351 | 
 | 
|---|
| 352 | 
 | 
|---|
| 353 | function TParamMult.GetLast: String;
 | 
|---|
| 354 | begin
 | 
|---|
| 355 |   Result := '';
 | 
|---|
| 356 |   if (mMultiple.Count > 0) then
 | 
|---|
| 357 |     Result := mMultiple[mMultiple.Count-1];
 | 
|---|
| 358 | end;
 | 
|---|
| 359 | 
 | 
|---|
| 360 | 
 | 
|---|
| 361 |     /// <summary>
 | 
|---|
| 362 |     /// GetMultiple Returns the VALUE of the element whose subscript is passed.
 | 
|---|
| 363 |     /// </summary>
 | 
|---|
| 364 |     /// <param name:='index'></param>
 | 
|---|
| 365 |     /// <returns></returns>
 | 
|---|
| 366 | function TParamMult.GetMultiple(index: String): String;
 | 
|---|
| 367 | var
 | 
|---|
| 368 |   TryResult: String;
 | 
|---|
| 369 |   StrError: String;
 | 
|---|
| 370 | begin
 | 
|---|
| 371 |   tryResult := '';
 | 
|---|
| 372 |       try
 | 
|---|
| 373 |       begin
 | 
|---|
| 374 |         tryResult := mMultiple[index];
 | 
|---|
| 375 |       end;
 | 
|---|
| 376 |         // The requested string might not be in the string array in which case IndexOf will 
 | 
|---|
| 377 |         // return a -1 so lets handle this through an IndexOutOfRangeException
 | 
|---|
| 378 | //      catch (ArgumentOutOfRangeException)
 | 
|---|
| 379 |       except
 | 
|---|
| 380 |       begin
 | 
|---|
| 381 |         if (Name <> '') then
 | 
|---|
| 382 |           StrError :=Name
 | 
|---|
| 383 |         else
 | 
|---|
| 384 |           StrError := kMultInstanceNameDefault;
 | 
|---|
| 385 |         strError +:= StrError + '[' + index + ']'#0D#0A' is undefined';
 | 
|---|
| 386 | 
 | 
|---|
| 387 |         // There was a complicated way to attempt to find this data on exception
 | 
|---|
| 388 |         // in the Delphi unit trpcb.pas in the broker project under
 | 
|---|
| 389 |         // TMult.GetFMultiple. I did not understand this so I will throw an
 | 
|---|
| 390 |         // exception here. -Travis
 | 
|---|
| 391 | 
 | 
|---|
| 392 | //        throw( new ParamMultException(strError));
 | 
|---|
| 393 |       end;
 | 
|---|
| 394 |       finally
 | 
|---|
| 395 |       begin
 | 
|---|
| 396 |         result := tryResult;
 | 
|---|
| 397 |       end;
 | 
|---|
| 398 | end;
 | 
|---|
| 399 | 
 | 
|---|
| 400 | 
 | 
|---|
| 401 |     /// <summary>
 | 
|---|
| 402 |     /// SetMultiple Stores a new element in the multiple.  mMultiple (StringCollection) is the
 | 
|---|
| 403 |     /// structure, which is used to hold the subscript and value pair.  Subscript is stored as 
 | 
|---|
| 404 |     /// the String, value is stored as an object of the string.
 | 
|---|
| 405 |     /// </summary>
 | 
|---|
| 406 |     /// <param name:='index'></param>
 | 
|---|
| 407 |     /// <param name:='Value'></param>
 | 
|---|
| 408 | procedure TParamMult.SetMultiple(index, newElement: String):
 | 
|---|
| 409 | begin
 | 
|---|
| 410 |   mMultiple.Set(index,newElement);
 | 
|---|
| 411 | end;
 | 
|---|
| 412 | 
 | 
|---|
| 413 | end.
 | 
|---|
| 414 |  
 | 
|---|