mkxmlrpc.pp 26 KB

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