Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / pas2js / 2022-12

Pas2JS: compiler extension to validate XML/XSLT files

8 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Ondrej Pokorny

Hello!

A short background:

I've learned how to use Pas2JS to write web client applications. I developed for it a simple but very powerful method: 1.) The server sends JSON objects as responses.
2.) The client converts these JSON objects to XML and uses XSLT templates to generate XHTML for the browser.

XSLT is for Pas2JS project something like LFM for the LCL project (DFM for VCL). But even more powerful because all the captions, texts and values get automatically replaced with XSLT. That allows me to fill pages, forms and tables with data with just 2 calls simple calls (JSON2XML+XSLT).

I add these XSLT files directly into the LPI project and add them to resources with:
{$R *.xslt}
{$R uFormContainerActions.xslt}
etc.

Now I have a lot of XSLT files in the project that I edit within Lazarus itself and it works well! (I don't need any external editor.)

This brings me to my problem:

I want to validate these XSLT files on compile - I want to write an extension for it.

What I want: when the XSLT is not valid (unclosed tag, invalid character, ... whatsoever), the compilation should be aborted and Lazarus should jump to the error position in XSLT.

Where should I start? I see 2 possible routes:

1.) Pas2JS compiler extension: hook into handling {$R *.xslt} and abort compilation with an error in case of invalid xslt file. 2.) Lazarus extension: create a package that hooks into the Compile command and checks all XSLT/XML files in the project before the project is compiled with Pas2JS.

I would prefer (1) because it the XSLT don't have to be registered in the LPI project.

Is it possible to extend the Pas2JS compiler with a DLL that hooks into the? {$R} handler? Or if not, is it possible to create some system to write own Pas2JS compiler extensions? Or do you have any other recommendations how to solve this?

Thanks
Ondrej

Michael Van Canneyt
to Ondrej Pokorny
Ondrej Pokorny wrote 38 lines
Hello! A short background: I've learned how to use Pas2JS to write web client applications. I developed for it a simple but very powerful method: 1.) The server sends JSON objects as responses. 2.) The client converts these JSON objects to XML and uses XSLT templates to generate XHTML for the browser. XSLT is for Pas2JS project something like LFM for the LCL project (DFM for VCL). But even more powerful because all the captions, texts and values get automatically replaced with XSLT. That allows me to fill pages, forms and tables with data with just 2 calls simple calls (JSON2XML+XSLT). I add these XSLT files directly into the LPI project and add them to resources with: {$R *.xslt} {$R uFormContainerActions.xslt} etc. Now I have a lot of XSLT files in the project that I edit within Lazarus itself and it works well! (I don't need any external editor.) This brings me to my problem: I want to validate these XSLT files on compile - I want to write an extension for it. What I want: when the XSLT is not valid (unclosed tag, invalid character, ... whatsoever), the compilation should be aborted and Lazarus should jump to the error position in XSLT. Where should I start? I see 2 possible routes: 1.) Pas2JS compiler extension: hook into handling {$R *.xslt} and abort compilation with an error in case of invalid xslt file.

No, this is not acceptable.

Show quoted text 6 lines
2.) Lazarus extension: create a package that hooks into the Compile command and checks all XSLT/XML files in the project before the project is compiled with Pas2JS. I would prefer (1) because it the XSLT don't have to be registered in the LPI project.

No. pas2js must compile pascal code, end of story.

Michael.

Michael Van Canneyt
to Michael Van Canneyt
Michael Van Canneyt wrote 15 lines
> Where should I start? I see 2 possible routes: > > 1.) Pas2JS compiler extension: hook into handling {$R *.xslt} and abort > compilation with an error in case of invalid xslt file. No, this is not acceptable. > 2.) Lazarus extension: create a package that hooks into the Compile command > and checks all XSLT/XML files in the project before the project is compiled > with Pas2JS. > > I would prefer (1) because it the XSLT don't have to be registered in the > LPI project. No. pas2js must compile pascal code, end of story.

After reading my own reply, I didn't like the tone of what I wrote.

Sorry about that.

Little more background:
as I wrote, pas2js must compile code, no more, no less.

I've had proposals of generating/interpreting html, CSS: All proposals for which I understand why people ask that, but then there will be no limit.

So, the best course of action is to limit
pas2js to what it must do: compile code.

There is a preprocessor option, you can tell pas2js to preprocess the input file.

Expanding on that, concept is maybe a solution that could help you:

What we could envision is a preprocessor for the {$R} which can be specified on the command line, per extension. Something like: -FPxslt=path/to/your/tool.

This handler would receive 2 names:
- InputFileName
- OutputFileName
when it sees the {$R}, the compiler generates an output filename and calls the tool with the 2 filenames. When the tool is done, the compiler includes the generated file.
(you can just copy the file if you don't need additional processing)

What do you think about that approach ?

Michael.

Ondrej Pokorny
to Michael Van Canneyt
Michael Van Canneyt wrote 52 lines
On Fri, 30 Dec 2022, Michael Van Canneyt via Pas2js wrote: >> Where should I start? I see 2 possible routes: >> >> 1.) Pas2JS compiler extension: hook into handling {$R *.xslt} and >> abort compilation with an error in case of invalid xslt file. > > No, this is not acceptable. > >> 2.) Lazarus extension: create a package that hooks into the Compile >> command and checks all XSLT/XML files in the project before the >> project is compiled with Pas2JS. >> >> I would prefer (1) because it the XSLT don't have to be registered >> in the LPI project. > > No. pas2js must compile pascal code, end of story. After reading my own reply, I didn't like the tone of what I wrote. Sorry about that. Little more background: as I wrote, pas2js must compile code, no more, no less. I've had proposals of generating/interpreting html, CSS: All proposals for which I understand why people ask that, but then there will be no limit. So, the best course of action is to limit pas2js to what it must do: compile code. There is a preprocessor option, you can tell pas2js to preprocess the input file. Expanding on that, concept is maybe a solution that could help you: What we could envision is a preprocessor for the {$R} which can be specified on the command line, per extension. Something like: -FPxslt=path/to/your/tool. This handler would receive 2 names: - InputFileName - OutputFileName when it sees the {$R}, the compiler generates an output filename and calls the tool with the 2 filenames. When the tool is done, the compiler includes the generated file. (you can just copy the file if you don't need additional processing) What do you think about that approach ?

I think you are right that this is not a task for Pas2JS.

I will do it as a Lazarus package. This gives me more possibilities (well, I don't know yet how to do it but it is always possible to extend the IDEIntf API).

I will definitely write back about my success.

Thanks
Ondrej

Ondrej Pokorny
to Ondrej Pokorny
Ondrej Pokorny wrote
I will definitely write back about my success.

I got it working for project&package files: https://postimg.cc/fJQR1S4R

You find the package source code below for reference (maybe it is not 100% complete, but you should get the idea). Now, if somebody asks you to handle HTML/XML/CSS files you can refer them here ;)

Of course it would be possible to read all pascal files and extract the
{$R} directives from the files, but I am happy with this simple solution.

Ondrej

unit CheckXMLPkg;
{$mode objfpc}{$H+}
interface

uses
? Classes, SysUtils, LazIDEIntf, SrcEditorIntf, ProjectIntf, CodeTree, OXmlPDOM, OTextReadWrite;

type
 ? TEventContainer = class
 ? private
 ??? function CheckXMLFile(const aFileName: string): Boolean;
 ??? function CheckProjectXMLFiles(Sender: TObject): TModalResult;
 ??? function CheckPackageXMLFiles(APackage: TIDEPackage): TModalResult;
 ? end;
procedure Register;
implementation
function TEventContainer.CheckPackageXMLFiles(APackage: TIDEPackage):
TModalResult;
var
 ? I: Integer;
begin
 ? Result := mrOK;
 ? for I := 0 to APackage.FileCount-1 do
 ??? if not CheckXMLFile(APackage.Files[I].Filename) then
 ????? Result := mrCancel;
 ? if Result=mrCancel then
 ? begin
 ??? if IDEMessagesWindow.SelectFirstUrgentMessage(mluError, True) then
 ????? IDEMessagesWindow.OpenSelection;
 ? end;
end;
function TEventContainer.CheckProjectXMLFiles(Sender: TObject):
TModalResult;
var
 ? I: Integer;
 ? xFile: TLazProjectFile;
begin
 ? Result := mrOK;
 ? for I := 0 to LazProject1.FileCount-1 do
 ? begin
 ??? xFile := LazProject1.Files[I];
 ??? if xFile.IsPartOfProject and not CheckXMLFile(xFile.Filename) then
 ????? Result := mrCancel;
 ? end;
 ? if Result=mrCancel then
 ? begin
 ??? if IDEMessagesWindow.SelectFirstUrgentMessage(mluError, True) then
 ????? IDEMessagesWindow.OpenSelection;
 ? end;
end;
function TEventContainer.CheckXMLFile(const aFileName: string): Boolean;
var
 ? xXML: TXMLDocument;
 ? xExt: string;
begin
 ? xExt := ExtractFileExt(aFileName);
 ? if not(SameText(xExt, '.xslt') or SameText(xExt, '.xml')) then
 ??? Exit(True);
 ? // OXml used here, you can use your favorite XML parser
 ? xXML := TXMLDocument.Create;
 ? try
 ??? xXML.ReaderSettings.ErrorHandling := ehSilent;
 ??? try
 ????? Result := xXML.LoadFromFile(aFileName) and not
Assigned(xXML.ParseError);
 ??? except
 ????? on E: EFCreateError do
 ????? begin
 ??????? // ignore - file doesn't exist
 ??????? Exit(True);
 ????? end;
 ??? end;
 ??? if Assigned(xXML.ParseError) then
 ??? begin
 ????? IDEMessagesWindow.AddCustomMessage(mluError,
xXML.ParseError.Reason, aFileName, xXML.ParseError.Line,
xXML.ParseError.LinePos, '');
 ????? Beep;
 ??? end;
 ? finally
 ??? xXML.Free;
 ? end;
end;
procedure Register;
var
 ? EventContainer: TEventContainer = nil;
begin
LazarusIDE.AddHandlerOnProjectBuilding(@EventContainer.CheckProjectXMLFiles);
LazarusIDE.AddHandlerOnPackageBuilding(@EventContainer.CheckPackageXMLFiles);
end;
end.
Michael Van Canneyt
to Ondrej Pokorny: I think you are right that this is not a task for Pas2JS. I will do it as a Lazarus…
Ondrej Pokorny wrote 22 lines
> What we could envision is a preprocessor for the {$R} which can be > specified > on the command line, per extension. Something like: > -FPxslt=path/to/your/tool. > > This handler would receive 2 names: > - InputFileName > - OutputFileName > when it sees the {$R}, the compiler generates an output filename and calls > the tool with the 2 filenames. When the tool is done, the compiler includes > the generated file. (you can just copy the file if you don't need > additional processing) > > What do you think about that approach ? I think you are right that this is not a task for Pas2JS. I will do it as a Lazarus package. This gives me more possibilities (well, I don't know yet how to do it but it is always possible to extend the IDEIntf API). I will definitely write back about my success.

Please do.
I did already quite a lot in the pas2jscomponents package for HTML file support in pas2js, but maybe your additions will inspire me even more :)

Michael.

Ondrej Pokorny
to Michael Van Canneyt
Michael Van Canneyt wrote 12 lines
On Fri, 30 Dec 2022, Ondrej Pokorny via Pas2js wrote: > I think you are right that this is not a task for Pas2JS. > > I will do it as a Lazarus package. This gives me more possibilities > (well, I don't know yet how to do it but it is always possible to > extend the IDEIntf API). > > I will definitely write back about my success. Please do. I did already quite a lot in the pas2jscomponents package for HTML file support in pas2js, but maybe your additions will inspire me even more :)

I already sent it here yesterday. Check my email from 22:50.

Ondrej

Michael Van Canneyt
to Ondrej Pokorny
Ondrej Pokorny wrote 15 lines
Am 30.12.2022 um 23:14 schrieb Michael Van Canneyt via Pas2js: > On Fri, 30 Dec 2022, Ondrej Pokorny via Pas2js wrote: >> I think you are right that this is not a task for Pas2JS. >> >> I will do it as a Lazarus package. This gives me more possibilities (well, >> I don't know yet how to do it but it is always possible to extend the >> IDEIntf API). >> >> I will definitely write back about my success. > > Please do. I did already quite a lot in the pas2jscomponents package for > HTML file support in pas2js, but maybe your additions will inspire me even > more :) I already sent it here yesterday. Check my email from 22:50.

Yes, I noticed, our mails crossed.

I will use your technique to automatically update form clas definitions based on the html.

Thank you !

Michael;