/* JavaScript Development Utilities: Generically useful code for JavaScript development. http://sourceforge.net/projects/jsutil Copyright (C) 2003-2005, Jeff Epstein All rights reserved. Modifications: No Redistribution in binary form, with or without modifications, are permitted provided that the following conditions are met: * 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. * If modifications are made to source code then this license should indicate that fact in the "Modifications" section above. * Neither the author, nor the contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. [NOTE: This license contains NO advertising clause.] */ package jsutil; import xbn.programs.XBNStatic; import xbn.XBNObject; import xbn.array.AOOValid; import xbn.array.AOOVCAll; import xbn.array.VWObject; import xbn.array.primitive.ListPA; import xbn.array.primitive.LPAConfig; import xbn.output.OWFile; import xbn.output.OSysDotOut; import xbn.string.SOBStringBuffer; import xbn.string.UtilChar; import xbn.string.UtilString; import xbn.template.GapConfig; import xbn.template.OGConfig; import xbn.template.Template; import xbn.template.TOGThree; import xbn.template.TOGTwo; import xbn.template.TemplateFiller; import xbn.template.util.GetTFFile; import xbn.template.util.GetTOG2FFile; import xbn.template.util.GetTOG3FFile; import xbn.template.util.GetTOGFrom; import xbn.util.FLRFile; import xbn.util.RCLength; import java.io.File; import java.util.StringTokenizer; /**
Generates JavaDoc-like documentation for a set of JavaScript code.
Source code: GenerateJSDoc.java
///
') list of relative paths to every JavaScript file you want to document. Must contain the ending '.js', and be properly formatted (as defined below). The order of this list is the order in which the table-of-contents is printed. If you need to provide a heck-of-a-lot of files (which DOS doesn't like), then set this alternatively to [[file=full_path_of_file]]
. That file must contain each item on a separate line, with no blank lines. Line separator must equal System.getProperty("line.separator")
Every gap must be surrounded by \~G\~
and \~EG\~
. For example:
\~G\~all_toc_items\~EG\~
To display the tilde character ('\~
') literally, use '\\\~
'.
Must contain at least this one gap. May or may not contain others.
Gap | Filled with |
[The value of command-line parameter six] |
The accumulation of every 'JavaScript file table of contents item' Template result. |
js_file_toc_item.tmpl
)Must contain exactly this set of unique gap names:
Gap | Filled with |
rurl_overall_to_js_dir |
The url to link from the table of contents page to the directory in which all JavaScript documentation files exist, including the final slash ('/'). This is the value of command-line parameter five, exactly as you provided. |
js_file_no_post |
The name the JavaScript file, minus the '.js'. |
js_summary |
The first full line of the JS FILE OVERVIEW DOCUMENTATION block for this JavaScript file. |
js_file.html
)Must contain exactly this set of unique gap names:
Gap | Filled with |
rurl_js_dir_to_overall |
The relative url from the JS-directory to the overall template. This is the value of parameter eight, exactly as provided. |
js_file_no_post |
The name of the JavaScript file, excluding the '.js' postfix. |
js_summary |
The first full line of the JS FILE OVERVIEW DOCUMENTATION block for this JavaScript file. |
function_list |
Every function table of contents item, concatenated together. |
js_description |
The entire contents of the JS FILE OVERVIEW DOCUMENTATION block. |
all_function_sections |
Every function section, concatenated together. |
function_toc_item.tmpl
)Must contain exactly this set of unique gap names:
Gap | Filled with |
func_name |
The name of this function, minus parameters and parenthesis. |
func_summary |
The first line of the PUBLIC FUNCTION block. |
function_section.tmpl
)Must contain exactly this set of unique gap names:
Gap | Filled with |
js_file_no_post |
The name of the JavaScript file, excluding the '.js' postfix. |
func_name |
The name of this function, minus parameters and parenthesis. |
func_description |
The entire contents of the PUBLIC FUNCTION block. |
Assume these template files...
C:\\test_gjsdoc\\template\\
index.html
I choose to store this in the same directory, even though this is not requiredjs_file_toc_item.tmpl
js_file.html
function_toc_item.tmpl
function_section.tmpl
...and these JavaScript files:
C:\\test_gjsdoc\\js\\
util_string.js
utility.js
more_js\\
Command line
First copy the overall template from source to destination:
copy C:\\test_gjsdoc\\template\\index.html C:\\test_gjsdoc\\output\\index.html
And then:
java jsutil.GenerateJSDoc C:\\test_gjsdoc\\template\\ C:\\test_gjsdoc\\js\\ util_string.js///utility.js///more_js\\util_array.js C:\\test_gjsdoc\\output\\ C:\\test_gjsdoc\\output\\index.html main_all_js_file_toc_items > C:\\test_gjsdoc\\output\\gjsdoc.log
Here is the final output and the log.
GenerateJSDoc.java
, a multi-line comment is one that starts with a line, after trim()
-ing, equal to '/**
' and ending with a line, after trim()
-ing, equal to '**/
'. Any '/**
' or '**/
' that shares the line with other non-whitespace characters is ignored./*
' and ends with '*/
'. Either may be surrounded by non-whitespace. In GenerateJSDoc.java
, the following multi-line comments are both legal and safely ignored:/* Single-lined multi-line comment */
/* Single-lined multi-line comment **/
/** Single-lined multi-line comment **/
/** A single-lined mlc **/ /** Another s-l mlc **/
/*
Semi-multi-lined multi-line
comment **/
/*
Semi-multi-lined multi-line comment
***/
/** Semi-multi-lined multi-line comment
*/
GenerateJSDoc.java
, because exactly one of '/**
' or '**/
' has whitespace around it, but not the other (or the other does not have two asterisks):/* Single-lined multi-line comment
**/
/*
Single-lined multi-line comment
**/
/**
Single-lined multi-line comment **/
/**
Semi-multi-lined multi-line comment
*/
/**
Semi-multi-lined multi-line comment
***/
/** A single-lined mlc **/ /**
Another s-l mlc
**/
trim()
-ing, equal to 'JS FILE OVERVIEW DOCUMENTATION
'. This is considered the overview marker. There is only one overview block per JavaScript file. Any blocks or multi-line comments existing before the overview block is ignored (although checked for syntax). The second and subsequent overview blocks are ignored.trim()
-ing, equal to 'PUBLIC FUNCTION'
. This is considered the function marker. Any function block existing before the overview block is ignored (although they are still checked for syntax). Function block documentation (in the table of contents, and in the body) are output in the order they actually exist in the js file./**
' and the block marker are allowed.trim()
-ing with '<P>'
and '</P>'
, and must contain at least one character between the 'P' tags. There must be a summary line in each block. In other words, aside from the marker, at least one non-whitespace line (starting with '<P>' and ending with '</P>') must exist in each block.function myFunction{
'trim()
-ing, with . The last function signature line is the first line after (or the same line as the one in which) the function signature begins, that ends with (after trim()
-ing) an end brace: '{
'. A multi-line function signature is one in which 'function
' exists on a line before its end-brace. No matter what 'function
', the function name, and the open parenthesis must exist on the first line of the function signature.State descriptions and key:
bInMLC
(imlc
): Are we currently in a multi-line comment? A multi-line comment, in GenerateJSDoc.java
, is considered starting with a line, after trim()
-ming, entirely equal to /**
and ending with a line entirely equal to **/
bNeedOverviewBlock
(nov
): Has the overview block been fully retrieved? Function blocks existing before the overview block are ignored. Is initialized to true, and remains that way until the entire overview block is retrieved.bInOverviewBlock
(iov
): Are we in the midst of the overview block? This will only equal true when bInMLC
equals true, and the overview marker was found.bInFunctionBlock
(ifn
): Are we in the midst of a function block? This will only equal true when bInMLC
is true, bNeedOverviewBlock
is false, and the function marker was found.bNeedSummaryLine
(nsm
): Do we need the summary line for the overview/function block? Will only be true when both bInMLC
and bInOverviewBlock
-or-bInFunctionBlock
are true, but no non-whitespace lines have yet been found after the function/block marker.bNeedFuncSignature
(nfg
): Do we need to retrieve the function signature? Is set to true when bInFunctionBlock
is set to true, and will remain true until the function signature is completely retrieved after the end of the function block
1
|
'x' means true, no value means 'false'
|
The JavaScript file separator.
Equal to '///'
The JavaScript file overview block marker
Equal to 'JS FILE OVERVIEW DOCUMENTATION'
The Function block marker
Equal to 'PUBLIC FUNCTION'
The multi-line comment start delimiter.
Equal to '/' + '**'**
The multi-line comment end delimiter.
Equal to '**' + '/'
".length()) { throwAXGJSD(flrf.getFileName(), "This is the summary line, which must start with '
' or '
', end with '
' or '', and contain at least one character in those tags."); } //It has enough space for what's required. String sFirstThreeChars = sLTrmd.substring(0, "".length()).toUpperCase(); String sLastThreeChars = sLTrmd.substring(sLTrmd.length() - "
".length()).toUpperCase(); if(sFirstThreeChars.equals("") && sLastThreeChars.equals("
")) { //This is indeed the summary line. //Store the text between the paragraph tags //as the summary text. sSummaryLine = sLTrmd.substring("".length(), sLTrmd.lastIndexOf("
")); //Put the summary line also into the body. //In the body, whitespace and the paragraph //tags should be preserved. Therefore, use //the untrimmed sbLine, rather than the //trimmed sLTrmd. sobsbBlockBody.append(sbLine.toString()); //We have the summary line. Go on to the //body of this block. bNeedSummaryLine = false; continue; } throwAXGJSD(flrf.getFileName(), "After the marker, this line is the first non-whitespace line in the block, but a summary line, after trim()-ming, must start with '', end with '
' and contain at least one character between those paragraph tags."); } //We're in the body of the block, between the //summary line and sMLC_STR_STR_SLSH. Accumulate //the lines. Preserve the whitespace, so use the //untrimmed sbLine, rather than the trimmed sLTrmd. sobsbBlockBody.append(sbLine.toString()); continue; } //ELSE: // - bInMLC equals false *and* // - This line does not equal sMLC_STR_STR_SLSH *and* // - This line does not equal sMLC_SLSH_STR_STR *and* // - This is not the first (marker) line in the mlc *and* // - This is any line at all within an mlc or block. } if(bNeedOverviewBlock) { throwAXGJSD(flrf.getFileName(), "The end of the file has been reached, but no overview block exists"); } if(vwjsd.getJSDoc(vwjsd.getVector().size() - 1).getVWJSDocFunc().getVector().size() < 1) { throwAXGJSD(flrf.getFileName(), "No public function blocks in this JavaScript file."); } } private static final void getFunctionSignature(String s_fileName) { //This is the first line after a funciton block, //where the function signature must be. final String sFNC = "function"; if(!sLTrmd.startsWith(sFNC)) { throwAXGJSD(s_fileName, "The first line after the function block must contain the function signature"); } //This is the (first line in the) function //signature. It must start //'function myFunction(' char cAfterFunction = sLTrmd.charAt(sFNC.length()); if(cAfterFunction != ' ' && cAfterFunction != '\t') { throwAXGJSD(s_fileName, "The first character after 'function' must be a tab or space "); } int iIdxParenO = sLTrmd.indexOf("("); if(iIdxParenO == -1) { throwAXGJSD(s_fileName, "The first line of a function signature must contain at least 'function myFunction('"); } //The function name must be between (that //tab/space after) 'function' and the open //paren. sFuncName = sLTrmd.substring((sFNC.length() + 1), iIdxParenO).trim(); if(sFuncName.length() < 1) { throwAXGJSD(s_fileName, "The is no function name between 'function' and the open paren ('(')."); } if(!uString.isLetterDigitUnderscore(sFuncName) && uString.hasLettersOrInAOC(sFuncName.substring(0, 1), false, new char[] {'_'})) { throwAXGJSD(s_fileName, "The function name's first character must start with a letter or underscore. The rest must be letters, digits, or underscores."); } //Store the values... JSDoc jsd = vwjsd.getJSDoc(vwjsd.getVector().size() - 1); jsd.addJSDocFunc( new JSDocFunc(sFuncName, sSummaryLine, sobsbBlockBody.toString())); //...and initialize the temporary/state versions //of these values. sFuncName = null; sSummaryLine = null; sobsbBlockBody.deleteAll(); } private static final String getStatus(int i_dbgCounter) { String sStatus = i_dbgCounter + " [" + iLine + "/" + iLineMLC + "] "; if(bInMLC) { sStatus += "imlc "; } else { sStatus += " "; } if(bNeedOverviewBlock) { sStatus += "nov "; } else { sStatus += " "; } if(bInOverviewBlock) { sStatus += "iov "; } else { sStatus += " "; } if(bInFunctionBlock) { sStatus += "ifn "; } else { sStatus += " "; } if(bNeedSummaryLine) { sStatus += "nsm "; } else { sStatus += " "; } if(bNeedFuncSignature) { sStatus += "nfg "; } else { sStatus += " "; } return sStatus; } private static final void throwAXGJSD(String s_fileName, String s_message) { String sDiagnostics = ". Diagnostics:\n\t" + "JS FILE: '" + s_fileName + "'\n\t" + "iLine=" + iLine + "\n\t" + "iLineMLC=" + iLineMLC + "\n\t" + "bInMLC=" + bInMLC + "\n\t" + "bNeedOverviewBlock=" + bNeedOverviewBlock + "\n\t" + "bInOverviewBlock=" + bInOverviewBlock + "\n\t" + "bInFunctionBlock=" + bInFunctionBlock + "\n\t" + "bNeedSummaryLine=" + bNeedSummaryLine + "\n\t" + "bNeedFuncSignature=" + bNeedFuncSignature + "\n\t" + "iLine=" + iLine + "\n\t" + "iLineMLC=" + iLineMLC + "\n\t" + "sLTrmd='" + sLTrmd + "'\n\t" + "sSummaryLine='" + sSummaryLine + "'\n\t" + "sFuncName='" + sFuncName + "'\n\t" + "sobsbBlockBody=[" + sobsbBlockBody.toString() + "]"; throwAXS("GenerateJSDoc: " + s_message + sDiagnostics); } private static void addJSDoc(JSDoc js_doc) { if(js_doc == null) { throwAXS("GenerateJSDoc.addJSDoc: js_doc is null."); } vwjsd.addJSDoc(js_doc); } } class JSDoc extends XBNObject { private String sBasePath = null; private String sRelJSFile = null; private String sNameNoPost = null; private String sSummary = null; private String sHtmlBody = null; private VWJSDocFunc vwjsdf = new VWJSDocFunc(); //This class is not static, but I know it's going to be created one-at-a-time. // private static final AOOValid aoov = new AOOValid( // new AOOVCAll( // new RCLength(1))); // aoov.crashIfBad(this.getClass().getNameNoPost() + ".constructor", "a_jsdFunc", a_jsdFunc); /**The name of this JSDoc is everything following the final File.separator in s_relJSFileNoPostfix,
**/ public JSDoc(String s_basePath, String s_relJSFileNoPostfix, String s_ummaryLine, String s_postSmryHtml) { throwAXIfBadStr(s_basePath, "s_basePath", "constructor"); throwAXIfBadStr(s_relJSFileNoPostfix, "s_relJSFileNoPostfix", "constructor"); throwAXIfBadStr(s_ummaryLine, "s_ummaryLine", "constructor"); throwAXIfBadStr(s_postSmryHtml, "s_postSmryHtml", "constructor"); if(s_relJSFileNoPostfix.indexOf(".") != -1) { throwAX("constructor: s_relJSFileNoPostfix ('" + s_relJSFileNoPostfix + "') may not contain a dot ('.')."); } sBasePath = s_basePath; if(s_relJSFileNoPostfix.indexOf(File.separator) == -1) { sNameNoPost = s_relJSFileNoPostfix; } else { sNameNoPost = s_relJSFileNoPostfix.substring( (s_relJSFileNoPostfix.lastIndexOf(File.separator) + 1), s_relJSFileNoPostfix.length()); } sRelJSFile = s_relJSFileNoPostfix; sSummary = s_ummaryLine; sHtmlBody = s_postSmryHtml; sopl(sNameNoPost); } public void addJSDocFunc(JSDocFunc jsd_func) { throwAXIfNull(jsd_func, "jsd_func", "addJSDocFunc"); vwjsdf.addJSDocFunc(jsd_func); } public String getBasePath() { return sBasePath; } public String getRelJSFile() { return sRelJSFile; } public String getNameNoPost() { return sNameNoPost; } public String getSummary() { return sSummary; } public String getHtmlBody() { return sHtmlBody; } public VWJSDocFunc getVWJSDocFunc() { return vwjsdf; } public final String toString() { String sTs = "getBasePath()='" + getBasePath() + "'\n\t" + "getRelJSFile()='" + getRelJSFile() + "'\n\t" + "getNameNoPost()='" + getNameNoPost() + "'\n\t" + "getNameNoPost()='" + getNameNoPost() + "', " + "getSummary()='" + getSummary() + "'\n\t" + "getHtmlBody()=[" + getHtmlBody() + "]\n\t" + "getVWJSDocFunc()={"; for(int i = 0; i < getVWJSDocFunc().getVector().size(); i++) { if(i > 0) { sTs += ", "; } sTs += "[" + i + ": " + getVWJSDocFunc().getJSDocFunc(i).toString() + "]"; } sTs += "}"; return sTs; } } class JSDocFunc extends XBNObject { private String sName = null; private String sSummary = null; private String sHtmlBody = null; public JSDocFunc(String s_name, String s_ummaryLine, String s_postSmryHtml) { throwAXIfBadStr(s_name, "s_name", "constructor"); throwAXIfBadStr(s_ummaryLine, "s_ummaryLine", "constructor"); throwAXIfBadStr(s_postSmryHtml, "s_postSmryHtml", "constructor"); sName = s_name; sSummary = s_ummaryLine; sHtmlBody = s_postSmryHtml; sopl(" " + sName); } public String getName() { return sName; } public String getSummary() { return sSummary; } public String getHtmlBody() { return sHtmlBody; } public final String toString() { return "getName()='" + getName() + "', " + "getSummary()='" + getSummary() + "', " + "getHtmlBody()=[" + getHtmlBody() + "]"; } } class VWJSDocFunc extends VWObject { public final JSDocFunc[] getAOJSDocFunc() { Object[] ao = getAOObject(); if(ao == null) { return null; } if(ao.length == 0) { return (new JSDocFunc[0]); } //The array is at least one element in length. JSDocFunc[] aJSDocFunc = new JSDocFunc[ao.length]; for(int i = 0; i < aJSDocFunc.length; i++) { aJSDocFunc[i] = (JSDocFunc)ao[i]; } return aJSDocFunc; } public final JSDocFunc getJSDocFunc(int i_dx) { return (JSDocFunc)getObject(i_dx); } public final void addJSDocFunc(JSDocFunc jsd_func) { add(jsd_func); } } class VWJSDoc extends VWObject { public final JSDoc[] getAOJSDoc() { Object[] ao = getAOObject(); if(ao == null) { return null; } if(ao.length == 0) { return (new JSDoc[0]); } //The array is at least one element in length. JSDoc[] aJSDoc = new JSDoc[ao.length]; for(int i = 0; i < aJSDoc.length; i++) { aJSDoc[i] = (JSDoc)ao[i]; } return aJSDoc; } public final JSDoc getJSDoc(int i_dx) { return (JSDoc)getObject(i_dx); } public final void addJSDoc(JSDoc js_doc) { add(js_doc); } }