XML::XMetaL - Dispatch class for XML::XMetaL development framework
This document refers to version 0.50 of XML::XMetaL, released 1st of August 2003.
In XMetal .mcr files:
... <MACROS> <MACRO name="On_Macro_File_Load" key="" lang="PerlScript"> <![CDATA[ use XML::XMetaL; use XML::XMetaL::Custom::Bingo; my $handler = XML::XMetaL::Custom::Bingo->new(-application => $Application); $dispatcher = XML::XMetaL->new(-application => $Application); $dispatcher->add_handler( -system_identifier => "bingo.dtd", -handler => $handler ); $dispatcher->On_Macro_File_Load(); ]]> </MACRO> <MACRO name="On_Document_Save" key="" lang="PerlScript"> <![CDATA[ $dispatcher->On_Document_Save(); ]]> </MACRO> ... </MACROS>
In XMetaL .ctm files:
... <OnInsertElementList> <OnInsertElement> <Name>section</Name> <Lang>PerlScript</Lang> <InsertElemScript> <![CDATA[$dispatcher->ctm_section()]]> </InsertElemScript> </OnInsertElement> ... </OnInsertElementList> ...
XML::XMetaL is a framework for object oriented XMetaL development using Perl. The framework makes it easy to write Perl classes that customize XMetaL. The advantages of using the framework are:
Object oriented XMetaL application development, with nearly all code moved from XMetaL .mcr and .ctm files to Perl modules.
In addition to the advantages of object oriented development, this makes it possible to use any editor for XMetaL development, not just Visual Studio (XMetaL 4+), or the built in editor (XMetaL 1.0 - 3.1).
There is no longer any need to use global customization files, such as xmetal.mcr to share functionality over several customization packages. Instead, common functionality is factored out into Perl modules and reused only by those customizations that need them.
This reduces the risk of conflicts between different customizations installed on the same XMetaL client.
Using the framework it is possible to write automated unit and function tests for XMetaL applications using Test::More and other test frameworks.
Useful utility functions, including a word counter, id generator, common XMetaL constants, node iterator, and more.
The XML::XMetaL class is a dispatcher for XMetaL customization handlers. XML:XMetaL objects are singletons. There can be only one XML::XMetaL object instantiated at any one time.
If an XML::XMetaL object already exists, the constructor (
new) will just return the already existing object.
Customization handlers are registered and associated with a system identifier with the
When a method is called on a dispatcher (XML::XMetaL object), the dispatcher figures out which handler that should handle the call, and forwards the method call to the handler.
For example, calling
On_Document_Save on the dispatcher, will make the dispatcher call the
On_Document_Save method for the handler associated with the system identifier of the currently active document.
When the dispatcher calls a handler, the call is wrapped in an
eval block. If an exception is thrown by the handler, it will be caught by the dispatcher and the error message will be shown in an XMetaL Alert box.
<MACRO name="On_Macro_File_Load" key="" lang="PerlScript"> <![CDATA[ use XML::XMetaL; use XML::XMetaL::Custom::Bingo; my $handler = XML::XMetaL::Custom::Bingo->new(-application => $xmetal); $dispatcher = XML::XMetaL->new(-application => $Application); $dispatcher->add_handler( -system_identifier => "bingo.dtd", -handler => $handler ); $dispatcher->On_Macro_File_Load(); ]]> </MACRO>
The constructor call must be placed in the
On_Macro_File_Load macro in the .mcr file of an XMetaL customization package. This macro is executed when the XMetaL macro file is first loaded.
C<new(-application => $Application)>
The constructor takes a single argument, the XMetaL application object. The
$Application variable is instantiated automatically by XMetaL.
The XML::XMetaL objects are singletons, i.e. only one XML::XMetaL object can be instantiated. If the constructor is called a second time, it will just return the existing object.
$dispatcher->add_handler( -system_identifier => "bingo.dtd", -handler => "XML::XMetaL::Custom::Bingo" );
add_handler method adds a handler for a particular XMetaL customization package. The handler must be a subclass of XML::XMetaL::Base. The handler is associated with a system identifier.
The system identifier is used to determine which handler to use with a particular document instance.
None, but see the
AUTOLOAD subsection below.
Whenever a method is called on the dispatcher that does not exist, it is assumed that this is a method call that should be forwarded to a handler object.
AUTOLOAD generates a wrapper method that can forward the call to a handler and installs it in the symbol table. It then calls the newly created wrapper method.
The next time the method is called, it will already exist, and is executed directly, without going through the
The dispatch methods generated by
AUTOLOAD will wrap calls to handlers in
eval blocks. Errors trapped by the dispatch method will be displayed in an XMetaL Alert box. Handlers should preferably handle their own errors in a more graceful manner, but this provides a last ditch failsafe mechanism.
The Corel XMetaL XML editor must be installed.
A lot, I am sure.
Please send bug reports to <email@example.com>.
Setting up macro files in XMetaL can be tedious. The
gmcr.pl script automatically generates .mcr files containing the most common macro calls.
Output is to STDOUT by default, so it must be redirected to a file using the standard MSDOS redirection mechanism.
gmcr.pl bingo.dtd XML::XMetaL::Custom::Bingo >bingo.mcr
gmcr.pl [-m] [-g] [-h] sysid module >outfile.mcr sysid = The system identifier of the documents to be customized using the module module - The Perl module used to customize XMetaL -f - Add file events (Replaces XMetaL's default file open and save operations) -m - Add mouse events (may slow XMetaL down) -g - Add global macro events -h - Print this help message outfile.mcr - An XMetaL macro file
The following event types can not be generated using gmcr.pl:
There are lots of things in the pipeline. The following list is just a few highlights. Whether they will ever be anything more than neat ideas depends on two things: how much time I have to work on them, and whether anyone is interested. (If you are, drop me an email. It will definitely improve your chance of seeing these features anytime soon.)
With XMetaL it is possible to trigger a script when tags are to be inserted. This is a very powerful mechanism, allowing such things as automated id generation and the insertion of complex, context dependent markup structures.
However, this requires customizing .ctm files. Up to XMetaL 3.1, a customization editor was included with XMetaL. In XMetaL 4.0, the customization editor is available only in the developer version. Furthermore, the customization editor is now a Visual Studio plug-in.
This is bad news for everyone that does not use Visual Studio. It is also bad news for Visual Studio users that want to do object oriented, and/or Perl development, because the XMetaL plug-ins for Visual Studio support neither.
Fortunately, .ctm files are just XML files, and can be edited in XMetaL, just like any other XML file. Adding calls to the dispather manually is tedious though. It is also time consuming. Even worse, it is error prone, because if the handler methods change, the .ctm file isn't updated automatically. It is very easy to add code for handling an element, or class of elements, and then forget to add calls to the code. It is even easier to remove code, and then forget to remove the calls.
Enter the .ctm call generator. It will analyse handlers, look for methods named
ctm_elementname and add or remove method calls in the .ctm file as appropriate.
Whether the call generator will be just a Perl script, or a full blown XMetaL customization package in its own right, is still undecided. Whichever it will be, I hope to be able to include it in the next release.
Eventually, there will be some sort of support for Simple XLink, and possibly even Extended XLink support.
I will add the capability to identify handlers based on public identifiers as an alternative to identification by system identifier.
This is useful because, unlike system identifiers, public identifiers are not tied to a physical location. (Well, this is fibbing a bit. System identifiers can be either URLs or URNs. URLs point to a location. URNs identify DTDs by name, much as public identifiers do. Unfortunately, URNs aren't very well supported by most XML applications.)
Currently, the dispatcher can't handle documents using XML Schema and XML Schema Instance (XSI). This is because the dispatcher looks in the doctype node to get the system identifier, but it does not know about XSI and
xsi:schemaLocation attribute suffers from the same problems as the system identifier in a doctype: if it is a URL, pointing at a physical file, the document will not validate if the location of the schema is changed. If the URL is relative, even moving the document from one computer to another may make it impossible to validate.
URNs don't have this problem, but on the other hand, support for them isn't implemented in XMetaL.
The schemaLocation remapping functionality will make it possible to remap a URI that can't be resolved by XMetaL to a URL that can be resolved.
This might be done by implementing catalog file support in the remapping mechanism, or by some other mechanism.
A treewalker for XMetaL. The treewalker will be able to process nodes while traversing them top-down or bottom-up.
The Formatting object interface in XMetaL 4.0 is accessible through JScript only. This is unfortunate, but it can be fixed.
I plan to implement access to the following functions through Perl:
This will be a set of decorator objects that wrap XMetaL objects and add new functionality to them.
Class::Wrapper::XMetaL::Documents could add the capability to handle Perl file handles to the
Open method. It would be possible to add XLink and XInclude capabilities and other functionality that appear as if they are part of the standard XMetaL API.
Currently there is no standardised way to deploy XML::XMetaL itself, or XML::XMetaL based module packages at the same time as deploying the XMetaL application (.dtd, .css, .mcr, .ctm, .tbr, and other files).
So far, I have just cobbled together a Perl script for handling the installation on a case by case basis.
A better solution is needed, particularly where there are a lot of clients to be updated.
PerlMSI might be the thing to use, but there are other alternatives. When I come up with a generic deployment solution, it will certainly make its way into an XML::XMetaL update.
XInclude support is more of a "could come in handy" than a must have. At least for now.
Henrik Martensson, <firstname.lastname@example.org>
Copyright 2003 by Henrik Martensson
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.