mkxmlrpc.pp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. {
  2. $Id$
  3. Automatic XML-RPC wrapper generator
  4. Copyright (c) 2003 by
  5. Areca Systems GmbH / Sebastian Guenther, [email protected]
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. }
  12. program MkXMLRPC;
  13. uses SysUtils, Classes, PParser, PasTree, PasWrite;
  14. resourcestring
  15. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  16. SNoServerClassNameProvided =
  17. 'No server class name provided (use --serverclass=<name>)';
  18. SNoUnitNameProvided =
  19. 'No name for generated unit provided (use --unitname=<name>)';
  20. type
  21. TParserEngine = class(TPasTreeContainer)
  22. protected
  23. Modules, UsedModules: TList;
  24. CurModule: TPasModule;
  25. public
  26. constructor Create;
  27. destructor Destroy; override;
  28. function CreateElement(AClass: TPTreeElement; const AName: String;
  29. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  30. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  31. override;
  32. function FindElement(const AName: String): TPasElement; override;
  33. function FindModule(const AName: String): TPasModule; override;
  34. end;
  35. TServerClass = class
  36. Element: TPasClassType;
  37. ImplName: String;
  38. end;
  39. TRPCList = class
  40. constructor Create;
  41. destructor Destroy; override;
  42. procedure AddServerClass(const AClassName: String);
  43. ServerClasses: TList;
  44. UsedModules: TStringList;
  45. end;
  46. var
  47. Engine: TParserEngine;
  48. constructor TParserEngine.Create;
  49. begin
  50. inherited Create;
  51. Modules := TList.Create;
  52. UsedModules := TList.Create;
  53. end;
  54. destructor TParserEngine.Destroy;
  55. begin
  56. UsedModules.Free;
  57. Modules.Free;
  58. inherited Destroy;
  59. end;
  60. function TParserEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  61. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  62. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  63. begin
  64. Result := AClass.Create(AName, AParent);
  65. Result.Visibility := AVisibility;
  66. if AClass.InheritsFrom(TPasModule) then
  67. begin
  68. Modules.Add(Result);
  69. CurModule := TPasModule(Result);
  70. end;
  71. end;
  72. function TParserEngine.FindElement(const AName: String): TPasElement;
  73. function FindInModule(AModule: TPasModule; const LocalName: String): TPasElement;
  74. var
  75. l: TList;
  76. i, j: Integer;
  77. Found: Boolean;
  78. begin
  79. l := AModule.InterfaceSection.Declarations;
  80. for i := 0 to l.Count - 1 do
  81. begin
  82. Result := TPasElement(l[i]);
  83. if CompareText(Result.Name, LocalName) = 0 then
  84. begin
  85. Found := False;
  86. for j := 0 to UsedModules.Count - 1 do
  87. if CompareText(TPasModule(UsedModules[j]).Name, AModule.Name) = 0 then
  88. begin
  89. Found := True;
  90. break;
  91. end;
  92. if not Found then
  93. UsedModules.Add(AModule);
  94. exit;
  95. end;
  96. end;
  97. Result := nil;
  98. end;
  99. var
  100. i: Integer;
  101. //ModuleName, LocalName: String;
  102. Module: TPasElement;
  103. begin
  104. {!!!: Don't know if we ever will have to use the following:
  105. i := Pos('.', AName);
  106. if i <> 0 then
  107. begin
  108. WriteLn('Dot found in name: ', AName);
  109. Result := nil;
  110. end else
  111. begin}
  112. Result := FindInModule(CurModule, AName);
  113. if not Assigned(Result) then
  114. for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do
  115. begin
  116. Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);
  117. if Module.ClassType = TPasModule then
  118. begin
  119. Result := FindInModule(TPasModule(Module), AName);
  120. if Assigned(Result) then
  121. exit;
  122. end;
  123. end;
  124. {end;}
  125. end;
  126. function TParserEngine.FindModule(const AName: String): TPasModule;
  127. var
  128. i: Integer;
  129. begin
  130. for i := Modules.Count - 1 downto 0 do
  131. begin
  132. Result := TPasModule(Modules[i]);
  133. if CompareText(Result.Name, AName) = 0 then
  134. exit;
  135. end;
  136. Result := nil;
  137. end;
  138. constructor TRPCList.Create;
  139. begin
  140. ServerClasses := TList.Create;
  141. UsedModules := TStringList.Create;
  142. end;
  143. destructor TRPCList.Destroy;
  144. var
  145. i: Integer;
  146. begin
  147. UsedModules.Free;
  148. for i := 0 to ServerClasses.Count - 1 do
  149. TServerClass(ServerClasses[i]).Free;
  150. ServerClasses.Free;
  151. end;
  152. procedure TRPCList.AddServerClass(const AClassName: String);
  153. var
  154. Element: TPasClassType;
  155. ServerClass: TServerClass;
  156. begin
  157. Element := TPasClassType(Engine.FindElement(AClassName));
  158. if not Assigned(Element) then
  159. begin
  160. WriteLn(StdErr, 'Server class "', AClassName, '" not found!');
  161. Halt(3);
  162. end;
  163. if (not Element.InheritsFrom(TPasClassType)) or
  164. (Element.ObjKind <> okClass) then
  165. begin
  166. WriteLn('"', AClassName, '" is not a class!');
  167. Halt(4);
  168. end;
  169. ServerClass := TServerClass.Create;
  170. ServerClasses.Add(ServerClass);
  171. ServerClass.Element := Element;
  172. ServerClass.ImplName := Copy(Element.Name, 2, Length(Element.Name));
  173. UsedModules.Add(Element.GetModule.Name);
  174. end;
  175. var
  176. OutputFilename, UnitName: String;
  177. RPCList: TRPCList;
  178. procedure WriteClassServerSource(ServerClass: TPasClassType;
  179. ImplementationSection: TPasSection; Method, ProcImpl: TPasProcedureImpl;
  180. const MethodPrefix: String; NestingLevel: Integer);
  181. { Method: Main server method
  182. ProcImpl: Current procedure (may be identical with Method) }
  183. type
  184. TConversionInfo = record
  185. ConverterName, TypecastFunction: String;
  186. ArgIsParent: Boolean;
  187. end;
  188. function MakeStructConverter(AClass: TPasClassType;
  189. Referrer: TPasProcedureImpl): TPasProcedureImpl; forward;
  190. function MakeArrayConverter(Member, ArraySizeProp: TPasProperty;
  191. ProcessProc, Referrer: TPasProcedureImpl): TPasProcedureImpl; forward;
  192. function FindArraySizeProperty(AArrayProp: TPasProperty): TPasProperty;
  193. var
  194. i: Integer;
  195. Name: String;
  196. begin
  197. Name := Copy(AArrayProp.Name, 1, Length(AArrayProp.Name) - 1) + 'Count';
  198. for i := 0 to TPasClassType(AArrayProp.Parent).Members.Count - 1 do
  199. begin
  200. Result := TPasProperty(TPasClassType(AArrayProp.Parent).Members[i]);
  201. if (Result.ClassType = TPasProperty) and (Result.Visibility = visPublic)
  202. and (CompareStr(Result.Name, Name) = 0) then
  203. exit;
  204. end;
  205. Name := AArrayProp.Name + 'Count';
  206. for i := 0 to TPasClassType(AArrayProp.Parent).Members.Count - 1 do
  207. begin
  208. Result := TPasProperty(TPasClassType(AArrayProp.Parent).Members[i]);
  209. if (Result.ClassType = TPasProperty) and (Result.Visibility = visPublic)
  210. and (CompareStr(Result.Name, Name) = 0) then
  211. exit;
  212. end;
  213. Result := nil;
  214. end;
  215. function GetConversionInfo(Element: TPasElement;
  216. Referrer: TPasProcedureImpl): TConversionInfo;
  217. var
  218. s: String;
  219. ArraySizeProp: TPasProperty;
  220. begin
  221. FillChar(Result, SizeOf(Result), 0);
  222. Result.ArgIsParent := False;
  223. if Element.ClassType = TPasProperty then
  224. begin
  225. ArraySizeProp := FindArraySizeProperty(TPasProperty(Element));
  226. if Assigned(ArraySizeProp) then
  227. begin
  228. Result.ConverterName := MakeArrayConverter(TPasProperty(Element),
  229. ArraySizeProp, ProcImpl, Referrer).Name;
  230. Result.ArgIsParent := True;
  231. exit;
  232. end else
  233. Element := TPasProperty(Element).VarType;
  234. end;
  235. if Element.ClassType = TPasUnresolvedTypeRef then
  236. begin
  237. s := UpperCase(Element.Name);
  238. if (s = 'BYTE') or (s = 'SHORTINT') or (S = 'SMALLINT') or
  239. (s = 'INTEGER') or (s = 'LONGINT') or (s = 'CARDINAL') or
  240. (s = 'INT64') or (s = 'QUADWORD') then
  241. Result.ConverterName := 'AWriter.CreateIntValue'
  242. else if (s = 'BOOLEAN') or (s = 'WORDBOOL') or (s = 'LONGBOOL') then
  243. Result.ConverterName := 'AWriter.CreateBooleanValue'
  244. else if s = 'STRING' then
  245. Result.ConverterName := 'AWriter.CreateStringValue'
  246. else if (s = 'FLOAT') or (s = 'SINGLE') or (s = 'DOUBLE') or
  247. (s = 'EXTENDED') then
  248. Result.ConverterName := 'AWriter.CreateDoubleValue'
  249. else if s = 'TDATETIME' then
  250. Result.ConverterName := 'AWriter.CreateDateTimeValue';
  251. end else if Element.ClassType = TPasClassType then
  252. Result.ConverterName := MakeStructConverter(TPasClassType(Element), Referrer).Name
  253. else if Element.ClassType = TPasEnumType then
  254. begin
  255. Result.ConverterName := 'AWriter.CreateIntValue';
  256. Result.TypecastFunction := 'Ord';
  257. end;
  258. if Length(Result.ConverterName) = 0 then
  259. raise Exception.Create('Result type not supported: ' + Element.ClassName +
  260. ' ' + Element.Name);
  261. end;
  262. function MakeAccessor(ConversionInfo: TConversionInfo;
  263. const DataSource, ArrayIndex: String): String;
  264. begin
  265. Result := ConversionInfo.ConverterName + '(';
  266. if ConversionInfo.TypecastFunction <> '' then
  267. Result := Result + ConversionInfo.TypecastFunction + '(';
  268. Result := Result + DataSource;
  269. if ConversionInfo.TypecastFunction <> '' then
  270. Result := Result + ')';
  271. if ArrayIndex <> '' then
  272. Result := Result + '[' + ArrayIndex + ']';
  273. Result := Result + ')';
  274. end;
  275. function GetParseValueFnName(PasType: TPasType): String;
  276. var
  277. s: String;
  278. begin
  279. SetLength(Result, 0);
  280. if PasType.ClassType = TPasArgument then
  281. begin
  282. if TPasArgument(PasType).Access = argVar then
  283. raise Exception.Create('"var" arguments are not allowed');
  284. PasType := TPasArgument(PasType).ArgType;
  285. end;
  286. if PasType.ClassType = TPasUnresolvedTypeRef then
  287. begin
  288. s := UpperCase(PasType.Name);
  289. if (s = 'BYTE') or (s = 'SHORTINT') or (S = 'SMALLINT') or
  290. (s = 'INTEGER') or (s = 'LONGINT') or (s = 'CARDINAL') or
  291. (s = 'INT64') or (s = 'QUADWORD') then
  292. Result := 'Int'
  293. else if (s = 'BOOLEAN') or (s = 'WORDBOOL') or (s = 'LONGBOOL') then
  294. Result := 'Boolean'
  295. else if s = 'STRING' then
  296. Result := 'String'
  297. else if (s = 'FLOAT') or (s = 'SINGLE') or (s = 'DOUBLE') or
  298. (s = 'EXTENDED') then
  299. Result := 'Double'
  300. else if s = 'TDATETIME' then
  301. Result := 'DateTime';
  302. end;
  303. if Length(Result) = 0 then
  304. raise Exception.Create('Argument type not supported: ' +
  305. PasType.ClassName + ' ' + PasType.Name);
  306. end;
  307. function NeedLocalProc(const ProcName: String;
  308. Referrer: TPasProcedureImpl): TPasProcedureImpl;
  309. var
  310. i, j: Integer;
  311. begin
  312. for i := 0 to Method.Locals.Count - 1 do
  313. begin
  314. Result := TPasProcedureImpl(Method.Locals[i]);
  315. if Result.Name = ProcName then
  316. begin
  317. j := Method.Locals.IndexOf(Referrer);
  318. if (j >= 0) and (i >= j) then
  319. begin
  320. // Move existing converter to the top and exit
  321. Method.Locals.Delete(i);
  322. j := Method.Locals.IndexOf(ProcImpl);
  323. if j < 0 then
  324. j := 0;
  325. Method.Locals.Insert(j, Result);
  326. end;
  327. exit;
  328. end;
  329. end;
  330. Result := nil;
  331. end;
  332. function MakeStructConverter(AClass: TPasClassType;
  333. Referrer: TPasProcedureImpl): TPasProcedureImpl;
  334. var
  335. ConverterName, s: String;
  336. Commands: TPasImplCommands;
  337. i: Integer;
  338. LocalMember: TPasElement;
  339. ConversionInfo: TConversionInfo;
  340. begin
  341. ConverterName := 'Convert' + AClass.Name;
  342. Result := NeedLocalProc(ConverterName, Referrer);
  343. if Assigned(Result) then
  344. exit;
  345. Result := TPasProcedureImpl.Create(ConverterName, Method);
  346. i := Method.Locals.IndexOf(Referrer);
  347. if i < 0 then
  348. i := 0;
  349. Method.Locals.Insert(i, Result);
  350. Result.ProcType := TPasFunctionType.Create('', Result);
  351. Result.ProcType.CreateArgument('Inst', AClass.Name);
  352. TPasFunctionType(Result.ProcType).ResultEl :=
  353. TPasResultElement.Create('', Result);
  354. TPasFunctionType(Result.ProcType).ResultEl.ResultType :=
  355. TPasUnresolvedTypeRef.Create('TXMLRPCStruct', Result);
  356. Result.Body := TPasImplBlock.Create('', Result);
  357. Commands := Result.Body.AddCommands;
  358. Commands.Commands.Add('Result := AWriter.CreateStruct');
  359. for i := 0 to AClass.Members.Count - 1 do
  360. begin
  361. LocalMember := TPasElement(AClass.Members[i]);
  362. if LocalMember.ClassType = TPasProperty then
  363. begin
  364. ConversionInfo := GetConversionInfo(LocalMember, Result);
  365. if ConversionInfo.ArgIsParent then
  366. s := 'Inst'
  367. else
  368. s := 'Inst.' + LocalMember.Name;
  369. s := 'AWriter.AddStructMember(Result, ''' + LocalMember.Name + ''', ' +
  370. MakeAccessor(ConversionInfo, s, '') + ')';
  371. Commands.Commands.Add(s);
  372. end;
  373. end;
  374. end;
  375. function MakeArrayConverter(Member, ArraySizeProp: TPasProperty;
  376. ProcessProc, Referrer: TPasProcedureImpl): TPasProcedureImpl;
  377. var
  378. i: Integer;
  379. ConverterName, s: String;
  380. Commands: TPasImplCommands;
  381. VarMember: TPasVariable;
  382. ForLoop: TPasImplForLoop;
  383. ConversionInfo: TConversionInfo;
  384. begin
  385. ConverterName := 'Convert' + Member.Parent.Name + '_' + Member.Name;
  386. Result := NeedLocalProc(ConverterName, Referrer);
  387. if Assigned(Result) then
  388. exit;
  389. Result := TPasProcedureImpl.Create(ConverterName, Method);
  390. i := Method.Locals.IndexOf(Referrer);
  391. if i < 0 then
  392. i := 0;
  393. Method.Locals.Insert(i, Result);
  394. Result.ProcType := TPasFunctionType.Create('', Result);
  395. Result.ProcType.CreateArgument('Inst', Member.Parent.Name);
  396. TPasFunctionType(Result.ProcType).ResultEl :=
  397. TPasResultElement.Create('', Result);
  398. TPasFunctionType(Result.ProcType).ResultEl.ResultType :=
  399. TPasUnresolvedTypeRef.Create('TXMLRPCArray', Result);
  400. Result.Body := TPasImplBlock.Create('', Result);
  401. Commands := Result.Body.AddCommands;
  402. Commands.Commands.Add('Result := AWriter.CreateArray');
  403. VarMember := TPasVariable.Create('i', Result);
  404. Result.Locals.Add(VarMember);
  405. VarMember.VarType := TPasUnresolvedTypeRef.Create('Integer', VarMember);
  406. ForLoop := Result.Body.AddForLoop(TPasVariable.Create('i', Result),
  407. '0', MethodPrefix + ArraySizeProp.Name + ' - 1');
  408. ForLoop.Body := TPasImplCommand.Create('', ForLoop);
  409. ConversionInfo := GetConversionInfo(Member.VarType, Result);
  410. if ConversionInfo.ArgIsParent then
  411. s := 'Inst'
  412. else
  413. s := 'Inst.' + Member.Name + '[i]';
  414. s := 'AWriter.AddArrayElement(Result, ' +
  415. MakeAccessor(ConversionInfo, s, '') + ')';
  416. TPasImplCommand(ForLoop.Body).Command := s;
  417. end;
  418. function CreateDispatcher(VarType: TPasClassType;
  419. Referrer: TPasProcedureImpl): TPasProcedureImpl;
  420. var
  421. DispatcherName: String;
  422. begin
  423. DispatcherName := 'Dispatch' + VarType.Name;
  424. Result := NeedLocalProc(DispatcherName, Referrer);
  425. if Assigned(Result) then
  426. exit;
  427. // Create new dispatcher method
  428. Result := TPasProcedureImpl.Create(DispatcherName, Method);
  429. if ProcImpl = Method then
  430. Method.Locals.Insert(0, Result)
  431. else
  432. Method.Locals.Insert(Method.Locals.IndexOf(Referrer), Result);
  433. Result.ProcType := TPasProcedureType.Create('', Result);
  434. Result.ProcType.CreateArgument('Inst', VarType.Name);
  435. Result.ProcType.CreateArgument('Level', 'Integer');
  436. WriteClassServerSource(VarType,
  437. ImplementationSection, Method, Result, 'Inst.', NestingLevel + 1);
  438. end;
  439. var
  440. IfElse, ParentIfElse: TPasImplIfElse;
  441. procedure CreateBranch(const MethodName: String);
  442. begin
  443. if Assigned(ParentIfElse) then
  444. begin
  445. IfElse := TPasImplIfElse.Create('', ParentIfElse);
  446. ParentIfElse.ElseBranch := IfElse;
  447. end else
  448. begin
  449. IfElse := TPasImplIfElse.Create('', ProcImpl.Body);
  450. ProcImpl.Body.Elements.Add(IfElse);
  451. end;
  452. ParentIfElse := IfElse;
  453. IfElse.Condition := 's = ''' + UpperCase(MethodName) + '''';
  454. end;
  455. procedure ProcessMethodCall(Member: TPasProcedure);
  456. function MakeProcArgs(Args: TList): String;
  457. var
  458. i: Integer;
  459. begin
  460. if (not Assigned(Args)) or (Args.Count = 0) then
  461. Result := ''
  462. else
  463. begin
  464. Result := '(';
  465. for i := 0 to Args.Count - 1 do
  466. begin
  467. if i > 0 then
  468. Result := Result + ', ';
  469. Result := Result + 'AParser.GetPrev' + GetParseValueFnName(TPasType(Args[i]));
  470. end;
  471. Result := Result + ')';
  472. end;
  473. end;
  474. var
  475. Commands: TPasImplCommands;
  476. s: String;
  477. begin
  478. CreateBranch(Member.Name);
  479. Commands := TPasImplCommands.Create('', IfElse);
  480. IfElse.IfBranch := Commands;
  481. if TPasProcedure(Member).ProcType.Args.Count > 0 then
  482. Commands.Commands.Add('AParser.ResetValueCursor');
  483. if Member.ClassType = TPasProcedure then
  484. begin
  485. Commands.Commands.Add(MethodPrefix + Member.Name +
  486. MakeProcArgs(TPasProcedure(Member).ProcType.Args));
  487. Commands.Commands.Add('AWriter.WriteResponse(nil)');
  488. end else
  489. begin
  490. // function
  491. s := MethodPrefix + Member.Name +
  492. MakeProcArgs(TPasProcedure(Member).ProcType.Args);
  493. Commands.Commands.Add('AWriter.WriteResponse(' +
  494. MakeAccessor(GetConversionInfo(TPasFunctionType(TPasFunction(Member).
  495. ProcType).ResultEl.ResultType, ProcImpl), s, '') + ')');
  496. end;
  497. end;
  498. procedure ProcessProperty(Member: TPasProperty);
  499. var
  500. LocalIfElse: TPasImplIfElse;
  501. IsArray, IsStruct: Boolean;
  502. s, s2: String;
  503. Commands: TPasImplCommands;
  504. Command: TPasImplCommand;
  505. ConversionInfo: TConversionInfo;
  506. begin
  507. if Member.ReadAccessorName <> '' then
  508. begin
  509. CreateBranch('Get' + Member.Name);
  510. IsArray := (Member.Args.Count = 1) and
  511. Assigned(FindArraySizeProperty(Member));
  512. IsStruct := Member.VarType.ClassType = TPasClassType;
  513. if IsStruct then
  514. s := CreateDispatcher(TPasClassType(Member.VarType), ProcImpl).Name +
  515. '(' + MethodPrefix + Member.Name;
  516. if NestingLevel = 0 then
  517. s2 := '1'
  518. else
  519. s2 := 'Level + 1';
  520. if IsArray or (IsStruct and (Member.Args.Count = 0)) then
  521. begin
  522. LocalIfElse := TPasImplIfElse.Create('', IfElse);
  523. IfElse.IfBranch := LocalIfElse;
  524. LocalIfElse.Condition := 'APath.Count <= ' + s2;
  525. end;
  526. if IsStruct then
  527. if IsArray then
  528. begin
  529. LocalIfElse.IfBranch := TPasImplCommand.Create('', LocalIfElse);
  530. TPasImplCommand(LocalIfElse.IfBranch).Command :=
  531. 'AWriter.WriteResponse(' +
  532. MakeAccessor(GetConversionInfo(Member, ProcImpl),
  533. Copy(MethodPrefix, 1, Length(MethodPrefix) - 1), '') + ')';
  534. LocalIfElse.ElseBranch := TPasImplCommand.Create('', LocalIfElse);
  535. TPasImplCommand(LocalIfElse.ElseBranch).Command :=
  536. s + '[AParser.GetNext' +
  537. GetParseValueFnName(TPasArgument(Member.Args[0]).ArgType) + '], ' +
  538. s2 + ')';
  539. end else
  540. begin
  541. if Member.Args.Count = 0 then
  542. begin
  543. LocalIfElse.IfBranch := TPasImplCommand.Create('', LocalIfElse);
  544. TPasImplCommand(LocalIfElse.IfBranch).Command :=
  545. 'AWriter.WriteResponse(' +
  546. MakeAccessor(GetConversionInfo(Member, ProcImpl),
  547. MethodPrefix + Member.Name, '') + ')';
  548. LocalIfElse.ElseBranch := TPasImplCommand.Create('', LocalIfElse);
  549. TPasImplCommand(LocalIfElse.ElseBranch).Command := s + ', ' + s2 + ')';
  550. end else
  551. begin
  552. IfElse.IfBranch := TPasImplCommand.Create('', IfElse);
  553. TPasImplCommand(IfElse.IfBranch).Command := s + '[AParser.GetNext' +
  554. GetParseValueFnName(TPasArgument(Member.Args[0]).ArgType) + '], ' +
  555. s2 + ')';
  556. end;
  557. end
  558. else if IsArray then
  559. begin
  560. LocalIfElse.IfBranch := TPasImplCommand.Create('', LocalIfElse);
  561. TPasImplCommand(LocalIfElse.IfBranch).Command :=
  562. 'AWriter.WriteResponse(' +
  563. MakeAccessor(GetConversionInfo(Member, ProcImpl),
  564. Copy(MethodPrefix, 1, Length(MethodPrefix) - 1), '') + ')';
  565. LocalIfElse.ElseBranch := TPasImplCommand.Create('', LocalIfElse);
  566. TPasImplCommand(LocalIfElse.ElseBranch).Command :=
  567. 'AWriter.WriteResponse(' +
  568. MakeAccessor(GetConversionInfo(Member.VarType, ProcImpl),
  569. MethodPrefix + Member.Name, 'AParser.GetNext' +
  570. GetParseValueFnName(TPasArgument(Member.Args[0]).ArgType)) + ')';
  571. end else
  572. begin
  573. IfElse.IfBranch := TPasImplCommand.Create('', IfElse);
  574. TPasImplCommand(IfElse.IfBranch).Command := 'AWriter.WriteResponse(' +
  575. MakeAccessor(GetConversionInfo(Member.VarType, ProcImpl),
  576. MethodPrefix + Member.Name, '') + ')';
  577. end;
  578. end;
  579. if Member.WriteAccessorName <> '' then
  580. begin
  581. CreateBranch('Set' + Member.Name);
  582. Commands := TPasImplCommands.Create('', IfElse);
  583. IfElse.IfBranch := Commands;
  584. Commands.Commands.Add('// Not supported by mkxmlrpc yet');
  585. end;
  586. end;
  587. var
  588. VarMember: TPasVariable;
  589. i: Integer;
  590. Command: TPasImplCommand;
  591. Member: TPasElement;
  592. begin
  593. VarMember := TPasVariable.Create('s', ProcImpl);
  594. ProcImpl.Locals.Add(VarMember);
  595. VarMember.VarType := TPasUnresolvedTypeRef.Create('String', VarMember);
  596. ProcImpl.Body := TPasImplBlock.Create('', ProcImpl);
  597. if NestingLevel = 0 then
  598. ProcImpl.Body.AddCommand('s := APath[' + IntToStr(NestingLevel) + ']')
  599. else
  600. ProcImpl.Body.AddCommand('s := APath[Level]');
  601. ParentIfElse := nil;
  602. for i := 0 to ServerClass.Members.Count - 1 do
  603. begin
  604. Member := TPasElement(ServerClass.Members[i]);
  605. if Member.Visibility <> visPublic then
  606. continue;
  607. if (Member.ClassType = TPasProcedure) or (Member.ClassType = TPasFunction)
  608. then
  609. ProcessMethodCall(TPasProcedure(Member))
  610. else if Member.ClassType = TPasProperty then
  611. ProcessProperty(TPasProperty(Member))
  612. else if (Member.ClassType <> TPasConstructor) and
  613. (Member.ClassType <> TPasDestructor) then
  614. WriteLn('Warning: Unsupportet member type: ', Member.ElementTypeName);
  615. end;
  616. if Assigned(ParentIfElse) then
  617. begin
  618. Command := TPasImplCommand.Create('', ParentIfElse);
  619. ParentIfElse.ElseBranch := Command;
  620. end else
  621. begin
  622. Command := TPasImplCommand.Create('', ProcImpl.Body);
  623. ProcImpl.Body.Elements.Add(Command);
  624. end;
  625. Command.Command := 'AWriter.WriteFaultResponse(2, ''Invalid method name'')';
  626. end;
  627. procedure WriteFPCServerSource;
  628. var
  629. i, j: Integer;
  630. Module: TPasModule;
  631. InterfaceSection, ImplementationSection: TPasSection;
  632. VarMember: TPasVariable;
  633. PropertyMember: TPasProperty;
  634. ProcMember: TPasProcedure;
  635. Arg: TPasArgument;
  636. ServerClass: TPasClassType;
  637. Stream: TStream;
  638. ProcImpl: TPasProcedureImpl;
  639. Found: Boolean;
  640. begin
  641. Module := TPasModule.Create(UnitName, nil);
  642. try
  643. InterfaceSection := TPasSection.Create('', Module);
  644. Module.InterfaceSection := InterfaceSection;
  645. ImplementationSection := TPasSection.Create('', Module);
  646. Module.ImplementationSection := ImplementationSection;
  647. InterfaceSection.AddUnitToUsesList('Classes');
  648. InterfaceSection.AddUnitToUsesList('XMLRPC');
  649. for i := 0 to RPCList.UsedModules.Count - 1 do
  650. InterfaceSection.AddUnitToUsesList(RPCList.UsedModules[i]);
  651. for i := 0 to RPCList.ServerClasses.Count - 1 do
  652. with TServerClass(RPCList.ServerClasses[i]) do
  653. begin
  654. ServerClass := TPasClassType.Create('T' + ImplName + 'XMLRPCServlet',
  655. InterfaceSection);
  656. InterfaceSection.Declarations.Add(ServerClass);
  657. ServerClass.ObjKind := okClass;
  658. ServerClass.AncestorType :=
  659. TPasUnresolvedTypeRef.Create('TXMLRPCServlet', ServerClass);
  660. // Create private field which holds the implementation instance
  661. VarMember := TPasVariable.Create('F' + ImplName, ServerClass);
  662. VarMember.Visibility := visPrivate;
  663. VarMember.VarType := TPasUnresolvedTypeRef.Create(Element.Name, VarMember);
  664. ServerClass.Members.Add(VarMember);
  665. // Create dispatcher method
  666. ProcMember := TPasProcedure.Create('Dispatch', ServerClass);
  667. ProcMember.Visibility := visProtected;
  668. ProcMember.IsOverride := True;
  669. ProcMember.ProcType := TPasProcedureType.Create('', ProcMember);
  670. ProcMember.ProcType.CreateArgument('AParser', 'TXMLRPCParser').
  671. Visibility := visPublic;
  672. ProcMember.ProcType.CreateArgument('AWriter', 'TXMLRPCWriter').
  673. Visibility := visPublic;
  674. ProcMember.ProcType.CreateArgument('APath', 'TStrings').
  675. Visibility := visPublic;
  676. ServerClass.Members.Add(ProcMember);
  677. // Create published property for implementation instance
  678. PropertyMember := TPasProperty.Create(ImplName, ServerClass);
  679. PropertyMember.Visibility := visPublished;
  680. PropertyMember.VarType := VarMember.VarType;
  681. VarMember.VarType.AddRef;
  682. PropertyMember.ReadAccessorName := 'F' + ImplName;
  683. PropertyMember.WriteAccessorName := 'F' + ImplName;
  684. ServerClass.Members.Add(PropertyMember);
  685. // Create dispatcher implementation
  686. ProcImpl := TPasProcedureImpl.Create('Dispatch', ServerClass);
  687. ImplementationSection.Declarations.Add(ProcImpl);
  688. ProcImpl.ProcType := ProcMember.ProcType;
  689. ProcMember.ProcType.AddRef;
  690. ProcImpl.ProcType.AddRef;
  691. WriteClassServerSource(Element, ImplementationSection, ProcImpl,
  692. ProcImpl, ImplName + '.', 0);
  693. end;
  694. for i := 0 to Engine.UsedModules.Count - 1 do
  695. begin
  696. Found := False;
  697. for j := 0 to RPCList.UsedModules.Count - 1 do
  698. if CompareText(RPCList.UsedModules[j],
  699. TPasModule(Engine.UsedModules[i]).Name) = 0 then
  700. begin
  701. Found := True;
  702. break;
  703. end;
  704. if not Found then
  705. ImplementationSection.AddUnitToUsesList(
  706. TPasModule(Engine.UsedModules[i]).Name);
  707. end;
  708. Stream := THandleStream.Create(StdOutputHandle);
  709. try
  710. WritePasFile(Module, Stream);
  711. finally
  712. Stream.Free;
  713. end;
  714. Stream := TFileStream.Create(OutputFilename, fmCreate);
  715. try
  716. WritePasFile(Module, Stream);
  717. finally
  718. Stream.Free;
  719. end;
  720. finally
  721. Module.Free;
  722. end;
  723. end;
  724. var
  725. i, j: Integer;
  726. s, Cmd, Arg: String;
  727. InputFiles, ClassList: TStringList;
  728. begin
  729. InputFiles := TStringList.Create;
  730. ClassList := TStringList.Create;
  731. try
  732. for i := 1 to ParamCount do
  733. begin
  734. s := ParamStr(i);
  735. j := Pos('=', s);
  736. if j > 0 then
  737. begin
  738. Cmd := Copy(s, 1, j - 1);
  739. Arg := Copy(s, j + 1, Length(s));
  740. end else
  741. begin
  742. Cmd := s;
  743. SetLength(Arg, 0);
  744. end;
  745. if (Cmd = '-i') or (Cmd = '--input') then
  746. InputFiles.Add(Arg)
  747. else if Cmd = '--output' then
  748. OutputFilename := Arg
  749. else if Cmd = '--unitname' then
  750. UnitName := Arg
  751. else if Cmd = '--serverclass' then
  752. ClassList.Add(Arg)
  753. else
  754. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  755. end;
  756. if ClassList.Count = 0 then
  757. begin
  758. WriteLn(StdErr, SNoServerClassNameProvided);
  759. Halt(2);
  760. end;
  761. if UnitName = '' then
  762. begin
  763. WriteLn(StdErr, SNoUnitNameProvided);
  764. Halt(2);
  765. end;
  766. Engine := TParserEngine.Create;
  767. try
  768. // Engine.SetPackageName('XMLRPC');
  769. for i := 0 to InputFiles.Count - 1 do
  770. ParseSource(Engine, InputFiles[i], '', '');
  771. RPCList := TRPCList.Create;
  772. try
  773. for i := 0 to ClassList.Count - 1 do
  774. RPCList.AddServerClass(ClassList[i]);
  775. WriteFPCServerSource;
  776. finally
  777. RPCList.Free;
  778. end;
  779. finally
  780. Engine.Free;
  781. end;
  782. finally
  783. InputFiles.Free;
  784. ClassList.Free;
  785. end;
  786. end.
  787. {
  788. $Log$
  789. Revision 1.5 2004-02-02 16:53:07 sg
  790. * Small fix in GetConversionInfo (result was not fully cleared)
  791. Revision 1.4 2003/11/22 12:08:32 sg
  792. * Better error reporting with line numbers
  793. * Array properties: The size property now can just match the name of the
  794. array property as well; so it now also works without a plural "s" after
  795. the base name
  796. * Added support for TDateTime parameters and results
  797. Revision 1.3 2003/06/25 08:56:26 sg
  798. * Added support for reading set properties
  799. Revision 1.2 2003/06/12 19:00:53 michael
  800. * Supports usage of declarations from other units (as long as mkxmlrpc
  801. parsed these units due to a --input=unitname command)
  802. Revision 1.1 2003/04/26 16:42:10 sg
  803. * Added mkxmlrpc
  804. }