Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 02/08/19 in Posts

  1. It probably is lacking the `()` there which it sometimes needs when invoking a method that returns an invokable type.
  2. Actually, since Delphi 6+, the preferred method is to use $IF with the CompilerVersion and/or RTLVersion constants, instead of using $IFDEF with the VERxxx conditionals.
  3. Remy Lebeau

    What is the fastest way to check if a file exists?

    Why is GetFileAttributes the way old-timers test file existence?
  4. It's the little things that count, like a little praise I did good 🙂 Thank you! I made changes and added new Create ctor: constructor Create(Level: integer; const Parent, NodeName: string; const Arguments: TArray<TNodeArgument>); overload; I also changed private to strict private in TABCStructureNode, since the other class was able to access all fields directly instead of properties - the Code completion was confusing by allowing me access to FNodeLevel instead of just NodeLevel property: I also added AddNnode overload that calls new ctor, can you confirm this is OK - to use TABCStructureNode.Create in: procedure TABCFileParser.AddNode(Level: integer; const Parent, NodeName: string; const Arguments: TArray<TNodeArgument>); begin FABCStructure := FABCStructure + [TABCStructureNode.Create(Level, Parent, NodeName, Arguments)]; end; As for the TArray vs TList... I just like TArray, as I'm used to it. unit uParseABCFile2; interface uses System.Classes, Vcl.StdCtrls, Vcl.Dialogs; type TNodeArgument = record ArgName : string; ArgValue : string; end; // File structure basic block TABCStructureNode = class strict private FNodeLevel : Integer; FNodeParent : string; FNodeName : string; FArguments : TArray<TNodeArgument>; strict private function GetArgument(Index: integer): TNodeArgument; function GetArgumentCount: integer; function GetArgumentAsString(Index: Integer): string; public constructor Create; overload; constructor Create(Level: integer; const Parent, NodeName: string; const Arguments: TArray<TNodeArgument>); overload; property NodeLevel: integer read FNodeLevel write FNodeLevel; property NodeParent: string read FNodeParent write FNodeParent; property NodeName: string read FNodeName write FNodeName; // Arguments property ArgumentCount: integer read GetArgumentCount; property Arguments[Index: Integer]: TNodeArgument read GetArgument; procedure AddArgument(Argument: TNodeArgument); procedure DeleteArgument(Index: Integer); property ArgumentAsString[Index: Integer]: string read GetArgumentAsString; // ... end; TABCFileParser = class private FABCFileName: string; // File structure FABCStructure: TArray<TABCStructureNode>; public constructor Create; property ABCFileName: string read FABCFileName write FABCFileName; procedure ParseFile; // File structure procedure AddNode(NodeItem: TABCStructureNode); overload; procedure AddNode(Level: integer; const Parent, NodeName: string; const Arguments: TArray<TNodeArgument>); overload; // CORRECT METHOD FOR UI INTERFACE function GetStructureAsString: string; // Methods for non-UI mode procedure ABCStructureToFile(const aFileName: string); end; implementation { TABCStructure } constructor TABCStructureNode.Create; begin // ??? end; constructor TABCStructureNode.Create(Level: integer; const Parent, NodeName: string; const Arguments: TArray<TNodeArgument>); begin FNodeLevel := Level; FNodeParent := Parent; FNodeName := NodeName; FArguments := Arguments; end; function TABCStructureNode.GetArgument(Index: integer): TNodeArgument; begin Result := FArguments[Index]; end; procedure TABCStructureNode.AddArgument(Argument: TNodeArgument); begin FArguments := FArguments + [Argument]; end; procedure TABCStructureNode.DeleteArgument(Index: Integer); begin Delete(FArguments, Index, 1); end; function TABCStructureNode.GetArgumentCount: integer; begin Result := Length(FArguments); end; function TABCStructureNode.GetArgumentAsString(Index: Integer): string; begin Result := FArguments[Index].ArgName + ' = ' + FArguments[Index].ArgValue; end; { TABCFileParser } constructor TABCFileParser.Create; begin // add something, if needed in the future end; procedure TABCFileParser.ParseFile; var vNewItem : TABCStructureNode; vNewArgument1, vNewArgument2: TNodeArgument; vArguments1, vArguments2: TArray<TNodeArgument>; begin // Parse/Read ABC File - FABCFileName // demo data vNewArgument1.ArgName := 'Arg1'; vNewArgument1.ArgValue := 'Value1'; vNewArgument2.ArgName := 'Arg2'; vNewArgument2.ArgValue := 'Value2'; vArguments1 := TArray<TNodeArgument>.Create(vNewArgument1, vNewArgument2); vNewArgument1.ArgName := 'Arg3'; vNewArgument1.ArgValue := 'Value3'; vNewArgument2.ArgName := 'Arg4'; vNewArgument2.ArgValue := 'Value4'; vArguments2 := TArray<TNodeArgument>.Create(vNewArgument1, vNewArgument2); AddNode(0, 'Parent', 'ABC Node 1', vArguments1); AddNode(1, 'ABC Node 1', 'ABC Node 2', vArguments2); end; procedure TABCFileParser.AddNode(NodeItem: TABCStructureNode); begin FABCStructure := FABCStructure + [NodeItem]; end; procedure TABCFileParser.AddNode(Level: integer; const Parent, NodeName: string; const Arguments: TArray<TNodeArgument>); begin FABCStructure := FABCStructure + [TABCStructureNode.Create(Level, Parent, NodeName, Arguments)]; end; procedure TABCFileParser.ABCStructureToFile(const aFileName: string); begin // Save ABC Structure to File // ... end; function TABCFileParser.GetStructureAsString: string; var i,j: Integer; begin Result := ''; for i := Low(FABCStructure) to High(FABCStructure) do begin Result := Result + FABCStructure[i].NodeName; Result := Result + ' Arguments: '; for j := 0 to FABCStructure[i].ArgumentCount - 1 do Result := Result + FABCStructure[i].ArgumentAsString[j] + '; '; Result := Result + sLineBreak; end; end; end.
  5. That looks a LOT better! I'd fill out the constructors. The purpose of a ctor is to initialize the state of an object. When it's a simple item like what you've got, there might be multiple ctors: one would take no arguments and return an empty object, but initialize the contents anyway just to be safe. The other(s) would offer different combinations of parameters to allow you to create and set values in one statement. Instead, you create an empty object that's not initialized, then assign values to it separately. While that's fine, it's extra coding that can be eliminated. Also, you seem addicted to using TArray, even though the way you're using it is more like a TList. You never set the initial size anywhere, which appears to be unbounded. So why not use a TList instead? In ParseFile, you don't need try...finally because you don't want to delete the object you just created b/c it's being added to a list, as your comment suggests. If you delete it, you'll end up with a list full of pointers to freed objects. Consider this instead of that whole block: AddNode( TABCStructureNode.Create( 25, 'parent_of_25', 'contents of node 25' ) ); But, if you stare at that long enough, you start to think, "why not just do this instead?" AddNode( 25, 'parent_of_25', 'contents of node 25' ); where the TABCStructureNode is created implicitly by AddNode
  6. Relatedly and somewhat in the same vein I read an article yesterday about AirBnB and why they're now moving off of React native, basically too much churn and too many landmines, even if it works really well when it works.
  7. I imagine what is happening and I assume it to be expected behavior, although I admit one has to know how variable capturing works to get it. It is even documented (Variable Binding Mechanism) There is still a lot of guessing about what is THandlerClass, OnHandle and how AddHandler is defined and what it does with its parameter, so it is difficult to give a detailed analysis.
  8. David Heffernan

    Pitfalls of Anonymous methods and capture

    If we have no knowledge of the types involved in this code then for sure we can't know what's wrong with it.
  9. M.Joos

    Property editor - on the finest art

    Thanks a lot. I will look and eventually extend your code somewhat. I am really missing the OI expert from Uwe Schuster, that went along similar ideas that you have. So maybe I can come up with a similar solution than Uwe's expert. At least your code is a good starting point, so thanks a lot for sharing.
  10. Remy Lebeau

    How To HTTP POST in Delphi?

    Your TIdHTTP code is all wrong for this kind of POST request. Try something more like this instead: uses ..., IdGlobalProtocols, IdHTTP, IdSSLOpenSSL; procedure TForm3.Button2Click(Sender: TObject); var SoapMsg: string; PostData, ResponseData: TStream; begin // buid up this string however you want (XML library, etc) ... SoapMsg := '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">' + ' <soapenv:Header/>' + ' <soapenv:Body>' + ' <tem:Consulta>' + ' <!--Optional:-->' + ' <tem:expresionImpresa><![CDATA[?re=LSO1306189R5&rr=GACJ940911ASA&tt=4999.99&id=e7df3047-f8de-425d-b469-37abe5b4dabb]]></tem:expresionImpresa>' + ' </tem:Consulta>' + ' </soapenv:Body>' + '</soapenv:Envelope>'; ResponseData := TMemoryStream.Create; try PostData := TStringStream.Create(SoapMsg, TEncoding.UTF8); try IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1; IdHTTP1.HTTPOptions := IdHTTP1.HTTPOptions + [hoNoProtocolErrorException, hoWantProtocolErrorContent]; IdHTTP1.Request.ContentType := 'text/xml'; IdHTTP1.Request.Charset := 'utf-8'; IdHTTP1.Request.Accept := 'text/xml'; IdHTTP1.Request.CacheControl := 'no-cache'; IdHTTP1.Request.CustomHeaders.Values['SOAPAction'] := 'http://tempuri.org/IConsultaCFDIService/Consulta'; IdHTTP1.Post('https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc?wsdl', PostData, ResponseData); finally PostData.Free; end; Memo1.Lines.BeginUpdate; try Memo1.Lines.Add(Format('Response Code: %d', [IdHTTP1.ResponseCode])); Memo1.Lines.Add(Format('Response Text: %s', [IdHTTP1.ResponseText])); ResponseData.Position := 0; ReadStringsAsCharset(ResponseData, Memo1.Lines, IdHTTP1.Response.Charset); finally Memo1.Lines.EndUpdate; end; finally ResponseData.Free; end; end;
  11. I would probably take it a step further, to avoid repeatedly indexing into the array: var Idx: integer; Rec: ^TSomeRec; // [...] Idx := CountInMyArr; SetLength(MyArr, Idx + SomeDelta); Rec := @MyArr[Idx]; Rec.SomeField := SomeValue; // and repeated for each field in TSomeRec Inc(CountInMyArr);
  12. stijnsanders

    Version Control System

    I made my own diff just to get it the way I want it: DirDiff What I didn't want is the stuff that's the same for both twice on screen. The last re-work I did on it enabled diffs between 3 or more files, but that's still not exactly what's needed for conflict resolution. I've open-sourced it here: Github...
  13. Whoa. Spaghetti AND meatballs. 😮 Do NOT mix UI elements with data classes! I don't have time to unravel this mess for you. I'd start with your basic "item", which seems to be something like a TStringlist plus a name (that record thing). This could be a TUPLE of some sort. Alternatively, you could use the first entry of the stringlist to contain its own name, simply to avoid the additional structure just to have a separate identifier for it. But it may not even be necessary ... hang on. Your second level is you have a collection of these items. You use a TArray, but it could be a TList or even a TCollection. Whatever. It needs Add, Insert, Get, and Delete methods, and maybe a lookup of some kind (so you can find an "Item by name"). However, because of the simplicity of this, you could use one main TStringlist where the Strings[n] element is a 'name' and Objects[n] is a TStringlist. Then sl.IndexOf(some_name) -> n, and sl.Objects[n] -> the TStringlist (from your record). Viola! No need for a record or even an array. Just one TStringlist. Why use multiple structures when one will work fine for all needs? Technically speaking, while you could get by with just a single TStringlist, I'd wrap it into a class just to provide a simpler and more appropriate interface for it (eg., so you don't know how it's actually implemented). Memo.Lines is of type TStrings, so you'd want methods like these to interface nicely with TMemos: function TmyClass.GetItem( const nm : string ) : TStrings; var n : integer; begin Result := NIL; n := sl.IndexOf(nm); // assums 'sl' is the name of the main TStringlist in this object if (n >= 0) then Result := TStrings( sl.Objects[n] ) end; procedure TmyClass.SetItem( const nm : string; Value : TStrings ); var n : integer; sl2 : TStringlist; begin sl2 := GetItem(nm); if Assigned(sl2) then sl2.Assign(Value) else begin sl2 := TStringlist.Create(); sl2.Assign(Value); sl.AddObject( nm, sl2 ); end; end; Then when you need to get an item's value to a Memo, you'd use: memo1.Lines.Assign( myClassObj.GetItem( some_name ) ); // maybe do GetItem first to check for NIL before calling Assign in case Assign chokes if you give it NIL and to save the memo into an item buffer, you'd use: myClassObj.SetItem( some_name, memo1.Lines ); See how you're completely hiding the implementation, while providing an interface that makes using it very simple? And it knows nothing about UI elements, and yet works very simply with them. The class TmyClass is actually very simple, as it just contains a TStringlist (named 'sl' above). (Not DERIVED from it, but CONTAINS it.) The constructor would create it, and the destructor would destroy it. You'd have GetItem, SetItem, and maybe one or two other methods. That's it.
  14. @Incus J Using Application.ProcessMessages is not the correct way to test if the GUI is updated in a lengthy process. Put your lengthy operation in a separate thread and add a possibility to cancel it if the GUI detects such a request. Application.ProcessMessages can lead to many hard to find anomalies and program failures.
  15. function GuessDecimalSeparator(const Value: string): Char; { assumes that the decimal separator is the last one and does not appear more than once } var idx: Integer; begin idx := Value.LastIndexOfAny(['.', ',']); // possible decimal separators if (idx >= 0) then begin Result := Value.Chars[idx]; if Value.CountChar(Result) = 1 then Exit; end; Result := FormatSettings.DecimalSeparator; // Default end;
  16. Dmitry Arefiev

    Firedac and PostGreSQL Over SSL

    You need to use sslxxxx PostgreSQL connection parameters: https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-PARAMKEYWORDS And put them into FireDAC PGAdvanced connection parameter: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Connect_to_PostgreSQL_(FireDAC)#Connection_Definition_Parameters
  17. Attila Kovacs

    New features in GExperts

    I can't blame you. I have the same problem with GExperts.
×