/**
	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( String 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=0; i<filenames.length; i++)
		try {
			bshdoc( filenames[i] );
		} catch ( e ) {
			throw new RuntimeException( 
				"bshdoc: Error parsing file: "+filenames[i]+": "+e );
		}
	untab();
	print("</BshDoc>");
}

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