# $Id: N3GenMsgRegistry.py,v 1.18 2004/02/18 14:43:19 graham Exp $ # # RDF message header registry generator # # Uses an RDF-based description stored in an N3Model to generate # registry data files. Uses N3Report query and report generation. # #--------+---------+---------+---------+---------+---------+---------+---------+ # # Copyright (c) 2002, G. KLYNE # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #--------+---------+---------+---------+---------+---------+---------+---------+ # $Source: /file/cvsdev/PythonN3/N3GenMsgRegistry.py,v $ # $Author: graham $ # $Revision: 1.18 $ $Date: 2004/02/18 14:43:19 $ #--------+---------+---------+---------+---------+---------+---------+---------+ # 1 2 3 4 5 6 7 8 import os import sys import string import StringIO from N3Exception import N3Exception from N3Node import N3Node #from N3Statement import N3Statement #from N3Model import N3Vocab from N3Model import N3Model, N3PrefixError from N3Report import N3Report, N3ReportError #--------+---------+---------+---------+---------+---------+---------+---------+ # Define N3GenMsgRegistry package exception conditions # # Base class for exceptions # class N3GenMsgRegistryError( N3Exception ): """ Base class for N3Report generation errors. Attributes: message -- explanation of the error """ def __init__(self, message): N3Exception.__init__( self, "N3GenMsgRegistryError: "+message ) pass class N3CommandError( N3GenMsgRegistryError ): """ Invalid command line argument supplied Attributes: message -- explanation of the error """ def __init__(self, message): N3GenMsgRegistryError.__init__( self, "N3CommandError: "+message ) pass class N3QueryError( N3GenMsgRegistryError ): """ Error in query structure Attributes: message -- explanation of the error """ def __init__(self, message): N3GenMsgRegistryError.__init__( self, "N3QueryError: "+message ) pass class N3ReadError( N3GenMsgRegistryError ): """ Error reading data files Attributes: message -- explanation of the error """ def __init__(self, message, file): N3GenMsgRegistryError.__init__( self, "N3ReadError: "+message ) self.File = file pass class N3GenerateError( N3GenMsgRegistryError ): """ Error generating registry files Attributes: message -- explanation of the error """ def __init__(self, message): N3GenMsgRegistryError.__init__( self, "N3GenerateError: "+message ) pass #--------+---------+---------+---------+---------+---------+---------+---------+ # N3GenMsgRegistry - message header registry generation functions # class N3GenMsgRegistry: #--------------------------------------------------------------------------- # Registry generation model data #--------------------------------------------------------------------------- RegDef = ''' @prefix rdf: . @prefix rdfs: . @prefix foaf: . @prefix hdr: . @prefix rep: . @prefix hrep: . hrep:GenHeaders a rep:Report ; :- ( [ rep:cmd rep:if ; rep:defined "htmlname" ; rep:do hrep:HTMLReport ] [ rep:cmd rep:if ; rep:defined "xmlname" ; rep:do hrep:RFCReport ] ) . hrep:HTMLReport a rep:Report ; :- ( [ rep:cmd rep:open ; rep:chan "t" ; rep:file ( [ rep:var "path" ] "/" [ rep:var "htmlname" ] ) ] [ rep:cmd rep:write ; rep:chan "t" ; rep:data hrep:TableHead ] [ rep:cmd rep:for ; rep:pattern hrep:HdrProtoPattern ; rep:do ( [ rep:cmd rep:write ; rep:chan "t" ; rep:data hrep:TableItem ] [ rep:cmd rep:if ; rep:defined "entries" ; rep:do ( [ rep:cmd rep:if ; rep:pattern hrep:HdrDetailPattern ; rep:do ( [ rep:cmd rep:open ; rep:chan "e" ; rep:file ( [rep:var "path" ] "/" [rep:var "pname"] "/" [rep:var "name"] ".html" ) ] [ rep:do hrep:GenEntry ] [ rep:cmd rep:close ; rep:chan "e" ] ) ; rep:else ( [ rep:cmd rep:write ; rep:chan "t" ; rep:data hrep:NoDetail ] ) ] ) ] ) ; rep:else ( [ rep:cmd rep:write ; rep:chan "t" ; rep:data hrep:TableEmpty ] ) ] [ rep:cmd rep:write ; rep:chan "t" ; rep:data hrep:TableFoot ] [ rep:cmd rep:close ; rep:chan "t" ] ) . hrep:GenEntry a rep:Report ; :- ( [ rep:cmd rep:write ; rep:chan "e" ; rep:data hrep:HdrEntry1 ] [ rep:cmd rep:for ; rep:pattern hrep:HdrControllerPattern ; rep:do ( [ rep:cmd rep:write ; rep:chan "e" ; rep:data hrep:HdrEntryAuthor ] ) ; rep:sep ( [ rep:cmd rep:write ; rep:chan "e" ; rep:data hrep:HdrEntryAuthorSep ] ) ] [ rep:cmd rep:write ; rep:chan "e" ; rep:data hrep:HdrEntry3 ] ) . #### query patterns #### # General header field info hrep:HdrProtoPattern :- ( [ rep:var "header" ] [ rep:and ( [ rep:uri rdf:type ] [ rep:uri hdr:HeaderField ] ), ( [ rep:uri hdr:fieldName ] [ rep:var "name" ] ), ( [ rep:opt ( [ rep:uri hdr:label ] [ rep:var "purpose" ] ) ] ), ( [ rep:uri hdr:protocol ] [ rep:var "p" ] [ rep:and ( [ rep:uri hdr:protocolName] [ rep:var "pname" ] ), ( [ rep:uri hdr:specification] [ rep:var "ps" ] [ rep:uri hdr:document ] [ rep:var "psdocument" ] ) ] ) ] ) . # Detail header field info (augments basic header data) hrep:HdrDetailPattern :- ( [ rep:var "header" ] [ rep:and ( [ rep:uri hdr:status ] [ rep:var "status" ] ), ( [ rep:uri hdr:specification ] [ rep:var "hs" ] [ rep:and ( [ rep:uri hdr:document ] [ rep:var "hsdocument" ] ), ( [ rep:opt ( [ rep:uri hdr:section ] [ rep:var "hssection" ] ) ] ) ] ), ( [ rep:opt ( [ rep:uri hdr:comment ] [ rep:var "info" ] ) ] ) ] ) . # Controller/Author pattern separate: may be repeated hrep:HdrControllerPattern :- ( [ rep:var "header" ] [ rep:uri hdr:controller ] [ rep:and hrep:HdrPersonPattern ] ) . hrep:HdrAuthorPattern :- ( [ rep:var "header" ] [ rep:uri hdr:author ] [ rep:and hrep:HdrPersonPattern ] ) . hrep:HdrPersonPattern :- ( [ rep:var "pers" ] [ rep:and ( [ rep:uri foaf:name ] [ rep:var "aname"] ), ( [ rep:and ( [ rep:uri foaf:mbox ] [ rep:var "ambox"] ) ; rep:alt ( [ rep:uri foaf:homepage ] [ rep:var "ahomepage"] ) ] ), ( [ rep:opt ( [rep:uri foaf:organization] [rep:var "orgname"] ) ] ), ( [ rep:opt hrep:HdrPostalPattern ] ), ( [ rep:opt ( [rep:uri foaf:workplaceTel] [rep:var "wtel"] ) ] ), ( [ rep:opt ( [rep:uri foaf:workplaceFax] [rep:var "wfax"] ) ] ), ( [ rep:opt ( [rep:uri foaf:workplaceHomepage] [rep:var "wurl"] ) ] ) ] ) . hrep:HdrPostalPattern :- ( [rep:uri foaf:workplacePostal] [ rep:var "wp" ] [ rep:and ( [ rep:opt ( [ rep:uri foaf:building ] [ rep:var "wpbuilding" ] ) ] ), ( [ rep:opt ( [ rep:uri foaf:street1 ] [ rep:var "wpstreet1" ] ) ] ), ( [ rep:opt ( [ rep:uri foaf:street2 ] [ rep:var "wpstreet2" ] ) ] ), ( [ rep:opt ( [ rep:uri foaf:city ] [ rep:var "wpcity" ] ) ] ), ( [ rep:opt ( [ rep:uri foaf:area ] [ rep:var "wparea" ] ) ] ), ( [ rep:opt ( [ rep:uri foaf:postcode ] [ rep:var "wppostcode" ] ) ] ), ( [ rep:opt ( [ rep:uri foaf:country ] [ rep:var "wpcountry" ] ) ] ) ] ) . #### output templates #### hrep:TableHead :- ( "" rep:nl "" rep:nl "Table of header field registrations" rep:nl "" rep:nl "" rep:nl "

Table of header field registrations

" rep:nl "" rep:nl "" rep:nl "" rep:nl "" rep:nl ### "" rep:nl "" rep:nl ) . hrep:TableItem :- ( "" rep:nl "" rep:nl "" rep:nl "" rep:nl "" rep:nl ) . hrep:NoDetail :- ( "" rep:nl ) . hrep:TableFoot :- ( "

Header name

Protocol

Purpose

" [rep:var "name"] "" [rep:var "pname"] "" rep:nl [ rep:if [rep:defined "purpose"] ; rep:do ( [ rep:var "purpose" ] rep:nl ) ] "
No detail found for header field " [rep:var "name"] "
" rep:nl "" rep:nl "" rep:nl ) . hrep:TableEmpty :- ( "No header fields found" rep:nl ) . ######## hrep:HdrEntry1 :- ( "

Header field: " [rep:var "name"] "

" rep:nl [ rep:if [rep:defined "purpose"] ; rep:do ( "

" [rep:var "purpose"] "

" rep:nl ) ] "
" rep:nl "
Applicable protocol:
" [rep:var "pname"] " (" "" [rep:var "psdocument"] "" ")
" rep:nl "
Status:
" [rep:var "status"] "
" rep:nl "
Author/Change controller:
" rep:nl "
" rep:nl ) . hrep:HdrEntryAuthor :- ( [rep:var "aname"] [ rep:if [rep:defined "ambox"] ; rep:do ( " (" [rep:var "ambox"] ")" ) ] [ rep:if [rep:defined "ahomepage"] ; rep:do ( " (" [rep:var "ahomepage"] ")" ) ] "
" rep:nl [ rep:if [rep:defined "orgname"] ; rep:do ( [rep:var "orgname"] rep:trimws [rep:defer (",
" rep:nl)] ) ] [ rep:if [rep:defined "wpbuilding"] ; rep:do ( [rep:var "wpbuilding"] rep:trimws [rep:defer (",
" rep:nl)] ) ] [ rep:if [rep:defined "wpstreet1"] ; rep:do ( [rep:var "wpstreet1" ] rep:trimws [rep:defer ", " ] ) ] [ rep:if [rep:defined "wpstreet2"] ; rep:do ( [rep:var "wpstreet2" ] rep:trimws [rep:defer ", "] ) ] [ rep:if [rep:defined "wpcity"] ; rep:do ( [rep:flush (",
" rep:nl)] [rep:var "wpcity"] rep:trimws [rep:defer ", " ] ) ] [ rep:if [rep:defined "wparea"] ; rep:do ( [rep:var "wparea"] rep:trimws [rep:defer ", " ] ) ] [ rep:if [rep:defined "wppostcode"] ; rep:do ( [rep:var "wppostcode"] rep:trimws [rep:defer (", ")] ) ] [ rep:if [rep:defined "wpcountry"] ; rep:do ( [rep:var "wpcountry"] rep:trimws [rep:defer (", ")] ) ] [ rep:flush ( ".
" rep:nl ) ] [ rep:if [rep:defined "wtel"] ; rep:do ( "Tel: " [rep:var "wtel"] "
" rep:nl ) ] [ rep:if [rep:defined "wfax"] ; rep:do ( "Fax: " [rep:var "wfax"] "
" rep:nl ) ] [ rep:if [rep:defined "wurl"] ; rep:do ( "URL: " "" [rep:var "wurl"] "
" rep:nl ) ] ) . hrep:HdrEntryAuthorSep :- ( "
" rep:nl ) . hrep:HdrEntry3 :- ( "
" rep:nl "
Specification document(s):
" rep:nl "
" "" [ rep:var "hsdocument" ] "" [ rep:if [rep:defined "hssection"] ; rep:do ( " (" [ rep:var "hssection" ] ")" ) ] "
" rep:nl [ rep:if [rep:defined "info"] ; rep:do ( "
Related information:
" rep:nl "
" [ rep:var "info" ] "
" rep:nl ) ] "
" rep:nl ) . #### RFC 2629 report generation #### hrep:RFCReport a rep:Report ; :- ( [ rep:cmd rep:open ; rep:chan "x" ; rep:file ( [ rep:var "path" ] "/" [ rep:var "xmlname" ] ) ] [ rep:cmd rep:write ; rep:chan "x" ; rep:data hrep:RFCTableHead ] [ rep:cmd rep:for ; rep:pattern hrep:HdrProtoPattern ; rep:do ( [ rep:cmd rep:write ; rep:chan "x" ; rep:data hrep:RFCTableItem ] ) ; rep:else ( [ rep:cmd rep:write ; rep:chan "x" ; rep:data hrep:RFCTableEmpty ] ) ] [ rep:cmd rep:write ; rep:chan "x" ; rep:data hrep:RFCTableFoot ] [ rep:cmd rep:for ; rep:pattern hrep:HdrProtoPattern ; rep:do ( [ rep:cmd rep:debug ; rep:data ( "Processing " [rep:var "name"] ) ] [ rep:cmd rep:if ; rep:pattern hrep:HdrDetailPattern ; rep:do hrep:RFCEntry ] ) ] [ rep:cmd rep:close ; rep:chan "x" ] ) . hrep:RFCEntry a rep:Report ; :- ( [ rep:cmd rep:write ; rep:chan "x" ; rep:data hrep:RFCEntry1 ] [ rep:cmd rep:for ; rep:pattern hrep:HdrControllerPattern ; rep:do ( [ rep:cmd rep:write ; rep:chan "x" ; rep:data hrep:RFCEntryAuthor ] ) ; rep:sep ( [ rep:cmd rep:write ; rep:chan "x" ; rep:data hrep:RFCEntryAuthorSep ] ) ] [ rep:cmd rep:write ; rep:chan "x" ; rep:data hrep:RFCEntry3 ] ) . hrep:RFCTableHead :- ( "
" rep:nl "
" rep:nl rep:nl ) . hrep:RFCTableEmpty :- ( "(No header fields found)" rep:nl ) . hrep:RFCEntry1 :- ( "
" rep:nl "" [ rep:if [rep:defined "purpose"] ; rep:do ( "" [rep:var "purpose"] "" rep:nl ) ] "" [rep:var "pname"] " (" [rep:var "psdocument"] ")" "" rep:nl "" [rep:var "status"] "" rep:nl "" ) . hrep:RFCEntryAuthor :- ( rep:nl "" [rep:var "aname"] [ rep:if [rep:defined "ambox"] ; rep:do ( " (" [rep:var "ambox"] ")" ) ] [ rep:if [rep:defined "ahomepage"] ; rep:do ( " (" [rep:var "ahomepage"] ")" ) ] rep:nl "" [ rep:if [rep:defined "orgname"] ; rep:do ( [rep:var "orgname"] rep:trimws [rep:defer ("," rep:nl)] ) ] [ rep:if [rep:defined "wpbuilding"] ; rep:do ( [rep:var "wpbuilding"] rep:trimws [rep:defer ("," rep:nl)] ) ] [ rep:if [rep:defined "wpstreet1"] ; rep:do ( [rep:var "wpstreet1" ] rep:trimws [rep:defer ", "] ) ] [ rep:if [rep:defined "wpstreet2"] ; rep:do ( [rep:var "wpstreet2" ] rep:trimws [rep:defer ", "] ) ] [ rep:if [rep:defined "wpcity"] ; rep:do ( [rep:flush ("," rep:nl "")] [rep:var "wpcity"] rep:trimws [rep:defer ", "] ) ] [ rep:if [rep:defined "wppostcode"] ; rep:do ( [rep:var "wppostcode"] rep:trimws [rep:defer ", "] ) ] [ rep:if [rep:defined "wpcountry"] ; rep:do ( [rep:var "wpcountry" ] rep:trimws [rep:defer ", "] ) ] [ rep:flush ("." rep:nl) ] [ rep:if [rep:defined "wtel"] ; rep:do ( "Tel: " [rep:var "wtel"] "" rep:nl ) ] [ rep:if [rep:defined "wfax"] ; rep:do ( "Fax: " [rep:var "wfax"] "" rep:nl ) ] [ rep:if [rep:defined "wurl"] ; rep:do ( "URL: " [rep:var "wurl"] "" "" rep:nl ) ] ) . hrep:RFCEntryAuthorSep :- ( "" rep:nl ) . hrep:RFCEntry3 :- ( "" rep:nl " " rep:nl "" [rep:var "hsdocument"] "" [ rep:if [rep:defined "hssection"] ; rep:do ( " (" [ rep:var "hssection" ] ")" ) ] "" rep:nl [ rep:if [rep:defined "info"] ; rep:do ( "" "" rep:nl [ rep:var "info" ] "" rep:nl ) ] "
" rep:nl ) . ''' Usage = ''' python N3GenMsgRegistry.py options Options are: -i file,file,... input files -o dir output directory -t name generate HTML table to named file -e generate HTML entries to subdirectories -x name generate RFC2629 source to named file ''' #--------------------------------------------------------------------------- # Setup methods #--------------------------------------------------------------------------- def __init__( self, argv ): """ Initialize and process command line options Parameters: argv list of command line arguments: -i file,file,... input files -o dir output directory -t name generate HTML table to named file -e generate HTML entries to subdirectories -x name generate RFC2629 source descriptions """ RegStr = StringIO.StringIO( N3GenMsgRegistry.RegDef ) self.Model = N3Model( RegStr ) #### # self.Model.setPrefixTable( None ) # self.Model.read( RegStr ) #### self.Files = [] self.Output = "" self.Table = None self.Entries = None self.RFC2629 = None i = 1 while i < len(argv): if argv[i] == "-i": i += 1 if i >= len(argv): raise N3CommandError( "Input file(s) expected" ) for f in string.split(argv[i],","): if not os.access( f, os.R_OK ): raise N3CommandError( "Cannot read file "+f ) self.Files.append( f ) elif argv[i] == "-o": i += 1 if i >= len(argv): raise N3CommandError( "Output directory expected" ) o = argv[i] if not os.access( o, os.W_OK ): raise N3CommandError( "Cannot write directory "+o ) self.Output = o elif argv[i] == "-t": i += 1 if i >= len(argv): raise N3CommandError( "HTML output filename expected" ) t = argv[i] self.Table = t elif argv[i] == "-e": self.Entries = 1 elif argv[i] == "-x": i += 1 if i >= len(argv): raise N3CommandError( "RFC 2629 output filename expected" ) x = argv[i] self.RFC2629 = x i += 1 # End while if not self.Model.parseOK(): raise N3QueryError( "Registry generator query error" ) return def readData( self ): """ Read data from files specified on command line. """ for f in self.Files: sys.stdout.write( "Reading file "+f+"...\n" ) s = open( f, "r" ) m = N3Model( s ) s.close() if not m.parseOK(): raise N3ReadError( "Invalid data file", f ) self.Model.merge( m ) return def makeRegistry( self, ReportNodeName ): """ Generate registry files from model data. """ hrep_Report = self.Model.makeNode( ReportNodeName ) Report = N3Report( self.Model, hrep_Report ) self.debug( "Output: "+`self.Output` ) VarBindings = { "file": N3Node( lit="TestReport.html" ), "path": N3Node( lit=self.Output ) } if self.RFC2629: VarBindings["xmlname"] = N3Node( lit=self.RFC2629 ) if self.Table: VarBindings["htmlname"] = N3Node( lit=self.Table ) if self.Entries: VarBindings["entries"] = N3Node( lit="1" ) Report.generate( VarBindings ) return def debug( self, msg ): sys.stdout.write( "N3GenMsgRegistry: "+msg+"\n" ) return # End of N3GenMsgRegistry #--------+---------+---------+---------+---------+---------+---------+---------+ # Stand-alone test code follows: # if __name__ == '__main__': # Process command line arguments sys.stdout.write( "Initializing\n" ) try: argv = sys.argv sys.stdout.write( "argv "+`argv`+", len="+`len(argv)`+"\n" ) if not argv or len(argv) <= 1: argv = [ "N3GenMsgRegistry", "-i", "N3GenMsgRegistryTest1.n3", "-o", ".", "-t", "N3GenMsgRegistryTest1.html" , "-e", "-x", "N3GenMsgRegistryTest1.xml" ] GenMsgRegistry = N3GenMsgRegistry( argv ) except N3CommandError, e: sys.stdout.write( str(e)+"\n" ) sys.stdout.write( N3GenMsgRegistry.Usage ) sys.exit( 1 ) except N3QueryError, e: sys.stdout.write( str(e)+"\n" ) sys.exit( 9 ) # Read model data sys.stdout.write( "Read data files\n" ) try: GenMsgRegistry.readData() except N3ReadError, e: sys.stdout.write( "Error reading file "+e.File+"\n" ) sys.stdout.write( str(e)+"\n" ) sys.exit( 2 ) except N3PrefixError, e: sys.stdout.write( str(e)+"\n" ) sys.exit( 2 ) # Generate registry data sys.stdout.write( "Generate registry data\n" ) try: GenMsgRegistry.makeRegistry( "hrep:GenHeaders" ) except N3GenerateError, e: sys.stdout.write( str(e)+"\n" ) sys.exit( 3 ) except N3ReportError, e: sys.stdout.write( str(e)+"\n" ) sys.exit( 3 ) sys.stdout.write( "Exiting\n" ) sys.exit( 0 ) #--------+---------+---------+---------+---------+---------+---------+---------+ # # $Log: N3GenMsgRegistry.py,v $ # Revision 1.18 2004/02/18 14:43:19 graham # Changes to header registry generation # # Revision 1.17 2002/06/05 15:26:33 graham # Option to trim spaces from formatted values # # Revision 1.16 2002/05/21 21:25:07 graham # Added separator feature to repeated patterns # # Revision 1.15 2002/05/21 20:50:37 graham # Fix error in XML output when comments missing # Allow multiple authors for header field # # Revision 1.14 2002/05/13 16:24:39 graham # Adaptations to run under Jython # # Revision 1.13 2002/05/13 08:26:29 graham # Allow homepage in place of mailbox # Label and comments fields now optional # # Revision 1.12 2002/05/10 11:49:19 graham # Cleaned up registry formatting # # Revision 1.11 2002/05/09 16:15:07 graham # Update registry data handling # # Revision 1.10 2002/05/07 10:54:16 graham # Fixes to accept N3 output: options to control default prefix table, accept 'this' as node name (treated as <#>), allow '-' in qnames (not strictly valid N3?) # # Revision 1.9 2002/05/01 15:43:48 graham # Cleanup small details # # Revision 1.8 2002/05/01 15:20:25 graham # Added capability to generate RFC 2629 documentation format for registry documents # # Revision 1.7 2002/04/28 17:01:45 graham # Partial port to Jython-21 (but having problems with os module support) # # Revision 1.6 2002/04/25 19:00:13 graham # Update CVS keyword fields # # Revision 1.5 2002/04/25 18:53:26 graham # Add copyright and disclaimer notices # # Revision 1.4 2002/04/25 05:56:56 graham # Added notes for further developments # # Revision 1.3 2002/04/24 17:59:00 graham # Message registry generator working # # Revision 1.2 2002/04/24 14:36:33 graham # Save edits # # Revision 1.1 2002/04/24 13:02:42 graham # Initial version of N3GenMsgRegistry #