#!/usr/bin/env /usr/bin/bsh
/**
    This is BeanShell documentation for the bshdoc script.
    bshdoc.bsh in conjunction with bshdoc.xsl supports javadoc style 
    documentation of BeanShell scripts and files.
    <p/>

    bshdoc reads a javadoc style comment and method signatures from one or
    more files and generates XML output representing their contents.  
    An XSL stylesheet bshcommands.xsl (supplied with the user manual source)
    can be used to render this to an indexed HTML page describing the commands.
    <p/>

    Method signatures may be supplied expliticly via javadoc style @method 
    tags in the comment.  For example you might do this to provide a more 
    verbose description for loosely typed arguments.  The bshcommands.xsl 
    stylesheet supplied with the user manual source will use the tag 
    signatures in lieu of autogenerated ones when they are present.  So you 
    can use this tag to determine exactly which methods from a file are 
    listed if you wish.
    <p/>

    Output goes to standard out.  Comments may include normal HTML tags.
    Javadoc style @tags are only recognized at the start of a line and 
    terminate the comment.
    <p/>

    Note: bshdoc generates full method and comment information for the file
    but bshdoc.xsl uses only file comments and method signatures to create
    the command index.
    <p/>

    The bshdoc command requires Java 1.4 for regular expressions.
    <p/>

    @method void bshdoc( String filename, String text ) 
    @method void bshdoc( String [] filenames ) 
*/
import bsh.*;
import java.util.regex.*;

// The ASTs are not currently public.  We'll probably move them to another
// package soon to do that.
setAccessibility(true);

// Trivial support for formatting the XML
tabs=0;
tab() { super.tabs++; }
untab() { super.tabs--; }
print( arg ) { 
    for(int i=0; i<tabs; i++)
        this.interpreter.print("  ");
    this.interpreter.println(String.valueOf(arg));
}

// Regexps used to parse the comment body.
//
// only on start of line
String tag = "\\n\\s*@(\\w+)";
// zero width lookahead to next tag or end of comment
String tagOrEndComment =
    "(?="+tag+"|\\*/)";
Pattern commentTextPattern = Pattern.compile(
    "(?s)/\\*\\*(.*?)"+tagOrEndComment );
Pattern tagsPattern = Pattern.compile(
    "(?s)"+tag+"\\s*(.*?)"+tagOrEndComment );

/**
    Print a comment section, with optional javadoc style tags
*/
printComment( String text ) 
{
    print("<Comment>");
    Matcher matcher = commentTextPattern.matcher( text );
    if ( matcher.find() )
        print("<Text><![CDATA["+ matcher.group(1) +"\n]]></Text>");

    print("<Tags>");
    Matcher matcher = tagsPattern.matcher( text );
    while ( matcher.find() )  {
        this.tagname=matcher.group(1);
        this.tagvalue=matcher.group(2);
        print("<"+tagname+">"+ tagvalue +"</"+tagname+">");
    }
    print("</Tags>");

    print("</Comment>");
}

/**
    bshdoc file comment.
*/
bshdoc( filename ) 
{
    print("<File>");
    tab();
    if ( filename.endsWith(".bsh") )
        this.name=filename.substring(0,filename.length()-4);
    if ( (i=name.lastIndexOf("/")) != -1 )
        this.name=name.substring(i+1);
    print("<Name>"+name+"</Name>");

    this.parser = new Parser( new FileReader(filename) );
    parser.setRetainComments(true);

    this.lastNode = null;
    this.firstComment = null;
    while( !parser.Line() )  
    {
        this.node = parser.popNode();

        if ( node instanceof BSHFormalComment 
            && firstComment == null )
            firstComment = node;

        if ( node instanceof BSHMethodDeclaration ) 
        {
            this.sig=node.getText();
            int i=sig.indexOf('{');
            if ( i > -1 )
                sig=sig.substring(0,i);
            print("<Method>");
            tab();
            print("<Name>"+node.name+"</Name>");
            print("<Sig>"+sig+"</Sig>");

            if ( lastNode instanceof BSHFormalComment ) {
                printComment( lastNode.text );

                if ( firstComment == lastNode )
                    firstComment = null;
            }
            untab();
            print("</Method>");
        }
        lastNode = node;
    }
    if ( firstComment != null ) {
        tab();
        printComment(firstComment.text);
        untab();
    }

    untab();
    print("</File>");
}

/**
    bshdoc list comment.
*/
bshdoc( String [] filenames ) 
{
    print("<?xml version="1.0\" encoding=\"UTF-8\"?>");
    print(
"<?xml-stylesheet type=\"text/xsl\" href=\"bshcommands.xsl\"?>");
    print(
"<!-- This file was auto-generated by the bshdoc.bsh script -->");
    print(
"<BshDoc>");
    
tab();
    for( 
int i=0i<filenames.lengthi++)
        try {
            
bshdocfilenames[i] );
        } catch ( 
) {
            throw new 
RuntimeException
                
"bshdoc: Error parsing file: "+filenames[i]+": "+);
        }
    
untab();
    print(
"</BshDoc>");
}

list=
bsh.args;
if ( list.
length == ) {
    print(
"usage: bshdoc file [ file ] [ ... ]");
    return;
}
bshdoc( list );