/* XBN Java: Generically useful, non-GUI Java code. http://sourceforge.net/projects/xbnjava Copyright (C) 1997-2003, 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 xbn.jdlcode; import xbn.XBNObject; import xbn.AssertException; import xbn.array.VWObject; import xbn.array.VWString; import xbn.array.BinarySearchData; import xbn.output.OConfig; import xbn.output.Outputter; import xbn.output.OWriter; import xbn.placeholder.i_i; import xbn.placeholder.s_s; import xbn.string.FLRString; import xbn.string.StringOrBuffer; import xbn.string.SOBStringBuffer; import xbn.string.TCInvisible; import xbn.string.UtilSOB; import xbn.string.UtilString; import xbn.string.UtilStringBuffer; import xbn.template.*; import xbn.template.util.UtilTemplate; import xbn.util.DirFile; import xbn.util.FLRFile; import xbn.util.ForLineRetrieval; import java.io.File; import java.io.FileFilter; import java.lang.reflect.*; import java.util.Vector; /**

Random functions used throughout the xbn.jdlcode package.

Source code:  UtilJDLCode.java.

@version 0.9b @author Jeff Epstein, http://sourceforge.net/projects/xbnjava. **/ public class UtilJDLCode extends XBNObject { //ENTIRE strings used four or more times. private static final String sPD = "."; private static final String sSLASH = "/"; private static final String sDOT_HTML = ".html"; /**

To shorten code and Javadoc.

Equal to "xbn.jdlcode.UtilJDLCode."

**/ public static final String sUD = "xbn.jdlcode.UtilJDLCode."; private UtilSOB uSOB = new UtilSOB(); private UtilStringBuffer uSB = new UtilStringBuffer(); private UtilString uStr = new UtilString(); private final TCInvisible trimInvis = new TCInvisible(); private UtilGap uGap = new UtilGap(); private UtilTemplate uTemplate = new UtilTemplate(); /**

Create a UtilJDLCode. This constructor does nothing.

**/ public UtilJDLCode() { } /**

Get the text used for the JavaDoc Link Code start gap.

@return JD **/ public static final String getJDLinkGapTextStart() { return "JD"; } /**

Get the text used for the JavaDoc Link Code end gap.

@return EJD **/ public static final String getJDLinkGapTextEnd() { return "EJD"; } /**

Get the JavaDoc Link Code start gap.

@return UtilGap.getTag(getJDLinkGapTextStart()) **/ public final String getJDLinkGapStart() { return uGap.getTag(getJDLinkGapTextStart()); } /**

Get the JavaDoc Link Code end gap.

@return UtilGap.getTag(getJDLinkGapTextEnd()) **/ public final String getJDLinkGapEnd() { return uGap.getTag(getJDLinkGapTextEnd()); } /**

How many "parts" do the provided JDFiles have in common, starting from the left?

@param jd_file1 The JDFile to compare against jd_file2. May not be null. @param jd_file2 The JDFile to compare against jd_file1. May not be null. @return For example:

If jd_file1 is... ...and jd_file2 is... ...then this is returned
xbn.jdlcode.JDFile xbn.jdlcode.JDFile 3
xbn.jdlcode.UtilJDLCode xbn.jdlcode.JDFile 2
xbn.array.VWObject xbn.array.UtilArray 1
overview-summary xbn.jdlcode.JDFile 0
xbn.jdlcode.subdoclet.SDClass xbn.jdlcode.JDFile 2
xbn.jdlcode.subdoclet.JDFile xbn.jdlcode.JDFile 2
xbn.jdlcode.JDFile xbn.jdlcode.subdoclet.JDFile 2

**/ public final int getSamePartCount(JDFile jd_file1, JDFile jd_file2) { int iPartsToCompare = -1; try { iPartsToCompare = jd_file2.getPartCount(); } catch(NullPointerException npx) { throwAX("getSamePartCount: jd_file2 is null."); } try { if(jd_file1.getPartCount() < jd_file2.getPartCount()) { iPartsToCompare = jd_file1.getPartCount(); } } catch(NullPointerException npx) { throwAX("getSamePartCount: jd_file1 is null."); } int iEqualParts = 0; for(int i = 0; i < iPartsToCompare; i++) { if(jd_file2.getPart(i).equals(jd_file1.getPart(i))) { iEqualParts++; } else { break; } } return iEqualParts; } /**

Get the relative url from one html file to another. This is only a link from one file to another, not to a location within the file.

@return The contents of the SOBStringBuffer: appendRelUrlToFile((new SOBStringBuffer("")), jdf_linkSource, jdc_toLinkTo) **/ public final String getRelUrlToFile(JDFile jdf_linkSource, JDClass jdc_toLinkTo) { SOBStringBuffer ssb = new SOBStringBuffer(sES); appendRelUrlToFile(ssb, jdf_linkSource, jdc_toLinkTo); return ssb.toString(); } /**

Append the relative url from one html file to another onto the StringBuffer.

Equal to appendRelUrlToFile([UtilStringBuffer].getSOBSB(str_buffer, sUD + "appendRelUrlToFile"), jdf_linkSource, jdc_toLinkTo) **/ public final void appendRelUrlToFile(StringBuffer str_buffer, JDFile jdf_linkSource, JDClass jdc_toLinkTo) { appendRelUrlToFile(uSB.getSOBSB(str_buffer, sUD + "appendRelUrlToFile"), jdf_linkSource, jdc_toLinkTo); } /**

Get the relative url from one html file to another onto the StringOrBuffer. This is only a link from one file to another, not to a location within the file.

See appendRelUrlToFile.

@param jdf_linkSource The JDFile representing the html file in which the link will reside. May not be null. @param jdc_toLinkTo The JDClass representing the html file the link should point to. Must not be primitive. @return The relative url from the html/class file represented by jf_linkFrom to the html/class file represented jdc_linkTo. For example:

From To Url to file
xbn.jdlcode.UtilJDLCode xbn.string.UtilString ~JD~utils~EJD~
xbn.string.UtilString xbn.array.VWString VWString.html
xbn.string.UtilString xbn.string.UtilString Empty string
xbn.jdlcode.UtilJDLCode java.lang.Object ~JD~o~EJD~
**/ public final void appendRelUrlToFile(StringOrBuffer str_orBfr, JDFile jdf_linkSource, JDClass jdc_toLinkTo) { throwAXIfNull(str_orBfr, "str_orBfr", "getRelUrlToFile"); throwAXIfNull(jdf_linkSource, "jdf_linkSource", "getRelUrlToFile"); throwAXIfNull(jdc_toLinkTo, "jdc_toLinkTo", "getRelUrlToFile"); if(jdc_toLinkTo.isPrimitive()) { throwAX("getRelUrlToFile: jdc_toLinkTo is primitive."); } if(jdf_linkSource == jdc_toLinkTo) { //The same java object, so the same file. return; } //Not the same java object. if(jdc_toLinkTo.isExternal()) { str_orBfr.append(jdc_toLinkTo.getExternalUrl()); str_orBfr.append(jdc_toLinkTo.getList(sSLASH)); str_orBfr.append(sDOT_HTML); return; } //We're linking to another local file. int iSameParts = -1; try { iSameParts = getSamePartCount(jdf_linkSource, jdc_toLinkTo); } catch(AssertException ax) { throwAX("getRelUrlToFile: jdc_toLinkTo is null."); } //First get the super directories from this file, to the //sub-most directory shared by this and the "to" one. if(iSameParts < jdf_linkSource.getDirCount()) { //These files are not in the same directory. We definitely need //some "../" str_orBfr.append(uStr.getDuped("../", (jdf_linkSource.getDirCount() - iSameParts))); } //Now get the link from that directory to the file itself. for(int i = iSameParts; i < (jdc_toLinkTo.getPartCount() - 1); i++) { str_orBfr.append(jdc_toLinkTo.getPart(i)); str_orBfr.append(sSLASH); } str_orBfr.append(jdc_toLinkTo.getPart(jdc_toLinkTo.getPartCount() - 1)); str_orBfr.append(sDOT_HTML); } /**

Get two strings from a single line in a class map configuration text file. All lines in the class map must be in the form.

[str_one][one_or_more_tabs][str_two]

@param s_cfgFileLine A single line from the configuration file. May not be null or zero characters in length. If there are two strings in this line, they must be divided by one or more tabs. @param s_callingFunc The function calling this one, for potential error messages only. @param i_lineNumber The line number of s_cfgFileLine, for potential error messages only. @param s_itemOneType The type description of string one. For potential errors messages only. @param s_itemTwoType The type description of string two. For potential errors messages only. @param b_twoPartsRqd If true, then there must be two strings in s_cfgFileLine, divided by at least one tab. If false, there may be one or two strings. @return An s_s where the first string is s1 and the second is s2. If no tabs exist, it is assumed that the entire value of this parameter is a single string. sTwo will be null. **/ public final s_s get2StringsFromLine(String s_cfgFileLine, String s_callingFunc, int i_lineNumber, String s_itemOneType, String s_itemTwoType, boolean b_twoPartsRqd) { if(s_cfgFileLine == null || s_cfgFileLine.length() < 1) { throwAX(s_callingFunc, i_lineNumber, "s_cfgFileLine must be non-null and at least one character in length."); } s_cfgFileLine = s_cfgFileLine.trim(); int iIdxOfTab = s_cfgFileLine.indexOf("\t"); if(b_twoPartsRqd && iIdxOfTab == -1) { throwAX(s_callingFunc, i_lineNumber, "There are no tabs in the line. At least one tab must separate the " + s_itemOneType + " from the " + s_itemTwoType + ". LINE: '" + uStr.getVisible(s_cfgFileLine) + "'"); } s_s ss = new s_s(); if(iIdxOfTab != -1) { ss.s1 = s_cfgFileLine.substring(0, iIdxOfTab); ss.s2 = s_cfgFileLine.substring((iIdxOfTab + 1), s_cfgFileLine.length()); ss.s2 = ss.s2.trim(); } else { ss.s1 = s_cfgFileLine; ss.s2 = null; } return ss; } /**

Get a new JDClass based on the two values in a line from the class map configuration text file. See getJDCAFromFLR.

@param s_cfgFileLine The line from the class map configuration text file. Must contain exactly two strings (at least one character each), divided by at least one tab. The first string is the abbreviation, and the second string is the fully qualified class name (or file name). Examples:
i			int
o			java.lang.Object
srq		javax.servlet.ServletRequest
pkgjdlc	xbn.jdlcode.package-summary
vwo		xbn.array.VWObject
@param i_lineNumber The line number of s_cfgFileLine, for potential error messages only. @param pt_array The PTArray containing all legal package name prefixes. @return (new JDClass([string1], [string2], pt_array)) **/ public final JDClass getJDClassFromLine(String s_cfgFileLine, int i_lineNumber, PTArray pt_array) { s_s ss = get2StringsFromLine(s_cfgFileLine, "getJDClassFromLine", i_lineNumber, "abbreviation", "fully qualified class name", true); return (new JDClass(ss.s1, ss.s2, pt_array)); } /**

Get a new PackageType based on the one-or-two values in a line from the class map configuration text file. See getJDCAFromFLR and PackageType.

@param s_cfgFileLine The line from the class map configuration text file. Must contain exactly two strings (at least one character each), divided by at least one tab. The first string is the PackageType, and the second string (if any) is the base url for that package's JavaDoc. Examples:
java		http://java.sun.com/j2se/1.3/docs/api/
javax		http://java.sun.com/products/servlet/2.2/javadoc/
random_util	file:/home/java_code/random_util/javadoc/
eckggg
xbn
@param i_lineNumber The line number of s_cfgFileLine, for potential error messages only. @param pt_array The PTArray containing all legal package name prefixes. @return (new PackageType([string1], [string2])) **/ public final PackageType getPackageTypeFromLine(String s_cfgFileLine, int i_lineNumber) { s_s ss = get2StringsFromLine(s_cfgFileLine, "getPackageTypeFromLine", i_lineNumber, "package prefix", "external url", false); return (new PackageType(ss.s1, ss.s2)); } /**

Create a JDCArray from a file.

@param s_mapFile The full path and file name to the class map configuration text file. Must be non-null, and point to an existing and readable text file. @return getJDCAFromFLR(new FLRFile(s_mapFile)) **/ public final JDCArray getJDCAFromMapFile(String s_mapFile) { throwAXIfBadStr(s_mapFile, "s_mapFile", "getJDCAFromMapFile"); return getJDCAFromFLR(new FLRFile(s_mapFile)); } /**

Create a JDCArray from the provided Doclet Class Map.

Example code  XmplUtilJDLCode_gjdcafflr.java.  Take a look at the actual XBN Doclet Class Map.

@param for_lineRetrieval The ForLineRetrieval containing the source text of the class map configuration text file. Must be in the following form:

[package_type_line1]
[package_type_line2]
[package_type_line3]
...

[divider line]

[class_map_line1]
[class_map_line2]
[class_map_line3]
...

Where...

There must be at least one package type that is local (in other words, has no referring url).

@exception AssertException The special PackageType "overview-summary", with an abbreviation of "over" is defined in this function. An exception is thrown if you also define either in your class map. **/ public final JDCArray getJDCAFromFLR(ForLineRetrieval for_lineRetrieval) { throwAXIfNull(for_lineRetrieval, "for_lineRetrieval", "getJDCAFromFLR"); VWObject acoJDC = new VWObject(); VWObject acoPT = new VWObject(); PTArray pta = null; boolean bFoundPTDivider = false; while(for_lineRetrieval.hasMoreLines()) { String sLine = for_lineRetrieval.getNextLine().toString(); sLine = sLine.trim(); if(sLine.length() > 0) { if(!bFoundPTDivider) { if(sLine.startsWith("~")) { if(acoPT.size() == 0) { throwAX("getJDCAFromFLR: No PackageTypes found before the '~' divider line. PackageTypes belong before it, and JDClasses after. At least one of each is required."); } Object[] ao = acoPT.getAOObject(); PackageType[] aPT = new PackageType[ao.length + 1]; for(int i = 0; i < ao.length; i++) { aPT[i] = (PackageType)ao[i]; } aPT[aPT.length - 1] = new PackageType("overview-summary", null); pta = new PTArray(aPT); bFoundPTDivider = true; } else { acoPT.add(getPackageTypeFromLine(sLine, for_lineRetrieval.getLineNumberPrev())); } } else { JDClass jdc = getJDClassFromLine(sLine, for_lineRetrieval.getLineNumberPrev(), pta); acoJDC.add(jdc); } } } if(!bFoundPTDivider) { throwAX("getJDCAFromFLR: So far, there are " + acoPT.size() + " PackageTypes, but I've reached the end of the file, and have not found the '~' divider line. PackageTypes belong before it, and JDClasses after. At least one of each is required."); } //Add the most special and reserved JDClass... JDClass jdc = getJDClassFromLine("over overview-summary", 1, pta); acoJDC.add(jdc); Object[] ao = acoJDC.getAOObject(); JDClass[] aJDC = new JDClass[ao.length]; for(int i = 0; i < aJDC.length; i++) { aJDC[i] = (JDClass)ao[i]; } return new JDCArray(aJDC, pta); } /**

Get the TLAObjects needed for getJavadocLine. See getJavadocLine.

The object is created each time this function is called.

@param optr_dbg The Outputter for debugging output. May not be null. **/ public final TLAObjects getTLAOForGJL(Outputter optr_dbg) { return (new TLAObjects( new TParseConfig( new GapConfig(getJDLinkGapTextStart(), getJDLinkGapTextEnd()), true, optr_dbg))); } /**

Given the provided line of Javadoc, return the same line with all JavaDoc Link Codes translated to actual urls.

Equal to getJavadocLine(s_line, jdf_current, jdc_array, b_cibJDLCTarget, b_ignoreIndirectJDLCs, (new TLAObjects()))

**/ public String getJavadocLine(String s_line, JDFile jdf_current, JDCArray jdc_array, boolean b_cibJDLCTarget, boolean b_ignoreIndirectJDLCs) throws TemplateFormatException { return getJavadocLine(s_line, jdf_current, jdc_array, b_cibJDLCTarget, b_ignoreIndirectJDLCs, (new TLAObjects())); } /**

Given the provided line of Javadoc, return the same line with all JavaDoc Link Codes translated to actual urls.

It is assumed that jdf_current is legal (represents an html file that actually exists). When called by xbn_doclet.XBNDoclet (via the JavaDoc command), it is. You could easily pass in an invalid one...

@param s_line The line of javadoc html. May not be null or zero characters in length. @param jdf_current The JDFile representing the file in which s_line exists. It is therefore the html file in which links will be "from". May not be null. Passed directly to getRelUrl. @param jdc_array The JDCArray containing all existing JDClasses (abbreviations and fully qualified class/file names.). May not be null. Passed directly to getRelUrl. @param b_ignoreIndirectJDLCs Passed directly to the SplitLinkCode constructor. @param tla_objects The TLAObjects provided directly to the TemplateLineAnalyzer constructor. May not be null. This also contains the Outputter used for debugging output. See getTLAOForGJL. **/ public final String getJavadocLine(String s_line, JDFile jdf_current, JDCArray jdc_array, boolean b_cibJDLCTarget, boolean b_ignoreIndirectJDLCs, TLAObjects tla_objects) throws TemplateFormatException { StringBuffer sbOutput = new StringBuffer(sES); FLRString flrsLine = new FLRString(s_line); while(flrsLine.hasMoreLines()) { StringBuffer sbLine = flrsLine.getNextLine(); TemplateLineAnalyzer tla = new TemplateLineAnalyzer(null, flrsLine.getLineNumberPrev(), sbLine, tla_objects); if(!tla.isDoneAnalyzing()) { while(tla.hasAnotherGap()) { s_s ss = tla.getNextSurrTxtAndGap(); sbOutput.append(ss.s1); appendLinkFromCode(sbOutput, ss.s2, jdf_current, jdc_array, b_cibJDLCTarget, b_ignoreIndirectJDLCs); } //The end of the line has been reached. sbOutput.append(tla.getFinalSurrTxt()); } } return sbOutput.toString(); } /**

[SLASH]**, alone on a line (after trimming tabs and spaces), is the start of a JavaDoc comment. **[SLASH], alone on a line (after trimming tabs and spaces), is the end of a JavaDoc comment. This may not exist in any function or code.

Use a single asterisk for internal (existing in functions), multi-lined documentation. For any other multi-line comments that exist outside of functions, but should still be ignored (such as license text), also use a single asterisk. This is not required (as long as the license text has nothing "illegal" in it), but will speed up the javadoc process.

**/ public final StringBuffer getJDTextFromJava(ForLineRetrieval flr_javaSourceCode) { throwAXIfNull(flr_javaSourceCode, "flr_javaSourceCode", "getJDTextFromJava"); StringBuffer sbJDText = new StringBuffer(sES); boolean bInJD = false; while(flr_javaSourceCode.hasMoreLines()) { StringBuffer sbLine = flr_javaSourceCode.getNextLine(); trimInvis.trim(sbLine); if(sbLine.length() == 3 && sbLine.charAt(0) == '/' && sbLine.charAt(1) == '*' && sbLine.charAt(2) == '*') { bInJD = true; //This is the start of a JD comment, but the starting line //itself needs to be discarded. continue; } else if(sbLine.length() == 3 && sbLine.charAt(0) == '*' && sbLine.charAt(1) == '*' && sbLine.charAt(2) == '/') { bInJD = false; //Discard the ending line, too. continue; } if(bInJD) { sbJDText.append(sbLine.toString()); } } return sbJDText; } /**

Get the Class for the provided fully-qualified name.

Equal to getClassForName(sUD + "getClassForName", s_fqClassName)

**/ public final Class getClassForName(String s_fqClassName) { return getClassForName(sUD + "getClassForName", s_fqClassName); } /**

Get the Class for the provided fully-qualified name. See Class.forName.

@param s_callingClsFnc The class-name-plus-function from which potential error messages should appear as coming from. @param s_fqClassName The fully-qualified class name to be analyzed. **/ public final Class getClassForName(String s_callingClsFnc, String s_fqClassName) { try { return Class.forName(s_fqClassName); } catch(ClassNotFoundException cnfx) { throwAXSpoof(s_callingClsFnc + ": s_fqClassName ('" + s_fqClassName + "') does not exist according to Class.forName()."); } //Never reached. Required for compile. return null; } //Used only by crashIfBadTarget() private Classes classes = new Classes(); /**

If the JavaDoc Link Code points to a non-existant target, die with a descriptive error message. Otherwise, do nothing.

Example code  XmplUtilJDLCode_cibt.java

Note: This validates JavaDoc Link Codes only, not hard-coded links (or portions thereof). So this link

<A HREF="\~JD\~getClassForName(s,s)\~EJD\~#bogus_name_link">my link</A>

is considered legal, because only the part within \~JD\~ and \~EJD\~ is recognized (and therefore analyzed).

As long as a JavaDoc Link Code exists somewhere within a JavaDoc block, they are all analyzed, even if they're not in the url of an "A HREF" tag.

@param jdf_linkSource Represents the html file in which the JavaDoc Link Code exists. May not be null, and hasNoJDLCodes must equal false. @param split_linkCode Holds the JavaDoc Link Code. May not be null, and every part (class, function/constructor, and parameters) must exist in jdc_array. If this contains no class name, then this link is local (it's target class is the same as the class in which the JavaDoc Link Code exists). Therefore, this link's target is jdf_linkSource.getList("."). @param jdc_array Contains information generated from the doclet class map. May not be null. @param b_ignoreIndirectJDLCs If true and jdf_linkSource.hasJDLCsIndirectly equals true and jdf_linkSource.hasJDLCsDirectly equals false, then this function does nothing. If this parameter is false, then indirect links are validated. No matter what, when jdf_linkSource.hasJDLCsDirectly equals true, the JavaDoc Link Code is always validated. **/ public final void crashIfBadTarget(JDFile jdf_linkSource, SplitLinkCode split_linkCode, JDCArray jdc_array, boolean b_ignoreIndirectJDLCs) { String sCIBL = sUD + "crashIfBadTarget: "; try { if(jdf_linkSource.hasNoJDLCodes()) { throwAXSpoof(sCIBL + "jdf_linkSource.hasNoJDLCodes() equals true. Extra information... jdf_linkSource.getList('.')='" + jdf_linkSource.getList(sPD) + "'."); } } catch(NullPointerException npx) { throwAXSpoof(sCIBL + "jdf_linkSource is null."); } //jdf_linkSource has links either directly or indirectly boolean bIndSrc = jdf_linkSource.hasJDLCsIndirectly(); if(bIndSrc && b_ignoreIndirectJDLCs) { //Secondary links may or may not be bad. We don't care. //They don't care. return; } //What is the target class name? String sTargetClass = null; try { if(split_linkCode.getClassName() == null) { //There is no class name in slc. Therefore, the //link's source and target file/class is one and the //same. //(The source file has links in it, either primarily or secondarily.) sTargetClass = jdf_linkSource.getList(sPD); if(!jdc_array.getJDCAFromFQCN().doesExist(sTargetClass)) { throwAXSpoof(sCIBL + "The link's target class ('" + sTargetClass + "') does not exist according to jdc_array.getJDCAFromFQCN().doesExist()." + (bIndSrc?" NOTE: jdf_linkSource.hasJDLCsIndirectly() equals true and b_ignoreIndirectJDLCs=false.":sES)); } } else { //There is a class name in SplitLinkCode. if(!jdc_array.doesExist(split_linkCode.getClassName())) { throwAXSpoof(sCIBL + "The target class (JavaDoc abbreviation='" + split_linkCode.getClassName() + "') does not exist in jdc_array."); } sTargetClass = jdc_array.getJDClass(split_linkCode.getClassName()).getList(sPD); } } catch(NullPointerException npx) { if(split_linkCode == null) { throwAXSpoof(sCIBL + "split_linkCode is null."); } throwAXSpoof(sCIBL + "jdc_array is null."); } //One special page-type to deal with: *-summary if(sTargetClass.endsWith("-summary")) { //This is a link to a -summary page, not a //class. These pages contain no functions or //constructors. if(split_linkCode.getCnstrFuncName() != null) { throwAXSpoof(sCIBL + "The link does contain a constructor or function, but no constructors or functions exist in *-summary pages (the target one for this particular link is '" + sTargetClass + "')."); } //The link has no constructor/function in it. GOOD. return; } //Ensure that the target page/class is one that exists in //jdc_array (in the jdlcode class map). try { if(!jdc_array.getJDCAFromFQCN().doesExist(sTargetClass)) { throwAXSpoof(sCIBL + "The link's target class ('" + sTargetClass + "') does not exist according to jdc_array.getJDCAFromFQCN().doesExist()."); } } catch(NullPointerException npx) { throwAXSpoof(sCIBL + "jdc_array is null."); } if(!classes.doesExist(sTargetClass)) { //We've not yet retrieved this class' information. Class cls = getClassForName(sUD + "crashIfBadTarget", sTargetClass); //The class does (actually) exist. classes.add(cls); //We now have all the public and protected constructors //and functions for this class. } if(split_linkCode.getCnstrFuncName() == null) { //This is a link to a(n overall) class, not a //constructor or function therein. GOOD. return; } //There is a constructor/function link. if(jdc_array.doesExist(split_linkCode.getCnstrFuncName()) && jdc_array.getJDClass(split_linkCode.getCnstrFuncName()).getName().equals(sTargetClass)) { //This is a link to a constructor. Constructor[] aConstructor = classes.getClassStuff(sTargetClass).aConstructors; for(int i = 0; i < aConstructor.length; i++) { if(hasSameParams(split_linkCode, aConstructor[i].getParameterTypes(), jdc_array)) { //Every parameter in split_linkCode and this //actually-existing constructor/function are //exactly the same. This target exists! return; } } throwAXSpoof(sCIBL + "The target class ('" + sTargetClass + "') does exist. But the target constructor has a parameter list ([" + split_linkCode.getParamList(",") + "]) that does not represent any actually-existing (public or protected) constructor."); } //This link is to a function. if(split_linkCode.getParameterCount() == 0) { throwAXSpoof(sCIBL + "This link contains only a zero-parameter function. It must be put outside of the JavaDoc Link Code (for example '~JD~class~EJD~#function()' instead of '~JD~class#function()~EJD~'). This is not caught by SplitLinkCode, because it cannot tell the difference between a constructor name, and a function name. However, I can. :' )"); } Method[] aMethod = classes.getClassStuff(sTargetClass).aMethods; boolean bFuncExists = false; for(int i = 0; i < aMethod.length; i++) { if(split_linkCode.getCnstrFuncName().equals(aMethod[i].getName())) { //The function names are the same... bFuncExists = true; if(hasSameParams(split_linkCode, aMethod[i].getParameterTypes(), jdc_array)) { //...and every parameter in split_linkCode and // this actually-existing // constructor/function are exactly the // same. This target exists! GOOD. return; } } } if(bFuncExists) { throwAXSpoof(sCIBL + "Both the target class ('" + sTargetClass + "') and function ('" + split_linkCode.getCnstrFuncName() + "') exist. But the parameter list ([" + split_linkCode.getParamList(",") + "]) does not represent any actually-existing (public or protected) version of that function."); } else { String sOthr = sES; if(jdc_array.doesExist(split_linkCode.getCnstrFuncName())) { sOthr = " The target function *is* equal to the abbreviation for another class: '" + jdc_array.getJDClass(split_linkCode.getCnstrFuncName()).getList(sPD) + "'."; } throwAXSpoof(sCIBL + "The target class ('" + sTargetClass + "') does exist, but the target function ('" + split_linkCode.getCnstrFuncName() + "') does not exist in that class." + sOthr); } } /**

Do the SplitLinkCode and Class array share the same exact parameter list (including order)?

@param split_linkCode The SplitLinkCode whose parameters should be compared to ac_parameters. May not be null. @param ac_parameters The array of Class objects, which should be compared to split_linkCode's parameter list. @param jdc_array The mapping of full class name to JavaDoc Link Code. May not be null. @return true If split_linkCode contains the same amount of parameters as ac_parameters.length, and every element X in split_linkCode represents the same class as element X in ac_parameters.
false If otherwise. **/ public final boolean hasSameParams(SplitLinkCode split_linkCode, Class[] ac_parameters, JDCArray jdc_array) { String sHSP = sUD + "hasSameParams: "; try { if(split_linkCode.getParameterCount() != ac_parameters.length) { //slc has a different number of parameters than ac_parameters. return false; } } catch(NullPointerException npx) { if(split_linkCode == null) { throwAXSpoof(sHSP + "split_linkCode is null."); } throwAXSpoof(sHSP + "ac_parameters is null."); } //...and has the same number of parameters as // the one we want... int j = 0; for(; j < ac_parameters.length; j++) { String sParamClass = null; try { if(!jdc_array.doesExist(split_linkCode.getParamName(j))) { throwAXSpoof(sHSP + "Parameter " + (j + 1) + " (JavaDoc abbreviation='" + split_linkCode.getParamName(j) + "') does not exist in jdc_array."); } sParamClass = jdc_array.getJDClass(split_linkCode.getParamName(j)).getList(sPD); } catch(NullPointerException npx) { throwAXSpoof(sHSP + "jdc_array is null."); } String sActualParam = ac_parameters[j].getName(); boolean bArray = false; if(sActualParam.charAt(0) == '[') { //It starts with "[" + SOME_CHARACTER and ends with ";". Strip! //SANITY CHECK...start // if(!ac_parameters[j].isArray() || // !Character.isUpperCase(sActualParam.charAt(1))) { // throwAXSpoof(sHSP + "SANITY CHECK FAILED. ac_parameters[" + j + "].getName() ('" + ac_parameters[j].getName() + "') is expected to be an array (isArray()=" + ac_parameters[j].isArray() + ") and the second character ('" + sActualParam.charAt(1) + "') is expected to be an uppercase letter."); // } //SANITY CHECK...end boolean bPrimitive = true; if(sActualParam.charAt(sActualParam.length() - 1) == ';') { //A primitive array "parameter" name is in the //form "[CAPITAL_LETTER" // //A non-primitive array "parameter" name is in //the form //"[CAPITAL_LETTER FULLY_QUALIFIED_CLASS_NAME;" bPrimitive = false; } //SANITY CHECK...start // if(bPrimitive && sActualParam.length() != 2) { // throwAXSpoof(sHSP + "SANITY CHECK FAILED. ac_parameters[" + j + "] (" + ac_parameters[j].getName() + ") is primitive, but not two characters in length."); // } //SANITY CHECK...end if(bPrimitive) { sActualParam = ac_parameters[j].getComponentType().getName(); } else { sActualParam = sActualParam.substring(2, (sActualParam.length() - 1)); } bArray = true; } if(sParamClass.equals(sActualParam) && split_linkCode.isParamArray(j) == bArray) { //...and parameter j is the same in both. } else { //...but parameter j is different. break; } } if(j != split_linkCode.getParameterCount()) { return false; } return true; } /**

Get the relative url (to the class-dot-function or constructor or local function), as defined by the parts existing in this SplitLinkCode.

@return getRelUrlAppended(SplitLinkCode split_linkCode, "", jdf_linkSource, jdc_array, b_cibJDLCTarget, b_ignoreIndirectJDLCs) **/ public final String getRelUrl(SplitLinkCode split_linkCode, JDFile jdf_linkSource, JDCArray jdc_array, boolean b_crashIfBadTarget, boolean b_ignoreIndirectJDLCs) { return getRelUrlAppended(split_linkCode, sES, jdf_linkSource, jdc_array, b_crashIfBadTarget, b_ignoreIndirectJDLCs); } /**

Get a copy of the string where the relative url (to the class-dot-function or constructor or local function), as defined by the parts existing in this SplitLinkCode, is appended.

@return The contents of getSOBSB: appendRelUrl(split_linkCode, [UtilString].getSOBSB(s_tr, sUD + "getRelUrlAppended"), jdf_linkSource, jdc_array, b_crashIfBadTarget, b_ignoreIndirectJDLCs)) **/ public final String getRelUrlAppended(SplitLinkCode split_linkCode, String s_tr, JDFile jdf_linkSource, JDCArray jdc_array, boolean b_crashIfBadTarget, boolean b_ignoreIndirectJDLCs) { SOBStringBuffer ssb = uStr.getSOBSB(s_tr, sUD + "getRelUrlAppended"); appendRelUrl(split_linkCode, ssb, jdf_linkSource, jdc_array, b_crashIfBadTarget, b_ignoreIndirectJDLCs); return ssb.toString(); } /**

Get a copy of the StringBuffer where the relative url (to the class-dot-function or constructor or local function), as defined by the parts existing in this SplitLinkCode, is appended.

@return The contents of getSOBSB: appendRelUrl(split_linkCode, [UtilStringBuffer].getSOBSB(str_buffer, sUD + "getRelUrlAppended"), jdf_linkSource, jdc_array, b_crashIfBadTarget, b_ignoreIndirectJDLCs)) **/ public final StringBuffer getRelUrlAppended(SplitLinkCode split_linkCode, StringBuffer str_buffer, JDFile jdf_linkSource, JDCArray jdc_array, boolean b_crashIfBadTarget, boolean b_ignoreIndirectJDLCs) { SOBStringBuffer ssb = uSB.getSOBSB(str_buffer, sUD + "getRelUrlAppended"); appendRelUrl(split_linkCode, ssb, jdf_linkSource, jdc_array, b_crashIfBadTarget, b_ignoreIndirectJDLCs); return ssb.getStringBuffer(); } /**

Append a relative url to the class-dot-function (or constructor or local function) represented by the parts existing in this SplitLinkCode.

Equal to appendRelUrl(split_linkCode, [UtilSOB].getSOBSB(str_orBfr, sUD + "getRelUrlAppended"), jdf_linkSource, jdc_array, b_crashIfBadTarget, b_ignoreIndirectJDLCs))

**/ public final void appendRelUrl(SplitLinkCode split_linkCode, StringBuffer str_buffer, JDFile jdf_linkSource, JDCArray jdc_array, boolean b_crashIfBadTarget, boolean b_ignoreIndirectJDLCs) { appendRelUrl(split_linkCode, uSB.getSOBSB(str_buffer, sUD + "appendRelUrl"), jdf_linkSource, jdc_array, b_crashIfBadTarget, b_ignoreIndirectJDLCs); } /**

Get a copy of the StringOrBuffer (as an SOBStringBuffer) where the relative url (to the class-dot-function or constructor or local function), as defined by the parts existing in this SplitLinkCode, is appended.

@return The contents of getSOBSB: appendRelUrl(split_linkCode, [UtilSOB].getSOBSB(str_orBfr, sUD + "getRelUrlAppended"), jdf_linkSource, jdc_array, b_crashIfBadTarget, b_ignoreIndirectJDLCs)) **/ public final SOBStringBuffer getRelUrlAppended(SplitLinkCode split_linkCode, StringOrBuffer str_orBfr, JDFile jdf_linkSource, JDCArray jdc_array, boolean b_crashIfBadTarget, boolean b_ignoreIndirectJDLCs) { SOBStringBuffer ssb = uSOB.getSOBSB(str_orBfr, sUD + "getRelUrlAppended"); appendRelUrl(split_linkCode, ssb, jdf_linkSource, jdc_array, b_crashIfBadTarget, b_ignoreIndirectJDLCs); return ssb; } /**

Append a relative url to the class-dot-function (or constructor or local function) represented by the parts existing in this SplitLinkCode.

@param split_linkCode The SplitLinkCode containing the JavaDoc Link Code. @param str_buffer The StringBuffer to append the link to. @param jdf_linkSource The JDFile representing the html file in which the link will exist. @param jdc_array The JDCArray containing all class abbreviations. @param b_crashIfBadTarget If true, then the link is checked to ensure that it's target truly exists, and crashes if it doesn't (see crashIfBadTarget). If false, then the link is not checked for validity (and b_ignoreIndirectJDLCs is ignored). @param b_ignoreIndirectJDLCs This parameter is only used when b_crashIfBadTarget equals true. If this parameter is true, then links on indirect link pages are not checked for validity. If false, then when links on indirect link pages are invalid, a crash occurs, with a descriptive error message.

Links found on inderict pages are actually the first sentence in your function/class/constructor's JavaDoc block (for example: "Append a relative url to the..." is the first sentence in this function's JavaDoc block). If they have a link to a function/constructor within the same class, then you would usually not put a class prefix in the JavaDoc Link Code (for example: \~JD\~crashIfBadTarget(jdf,slc,jdca,b)\~EJD\~ is the JavaDoc Link Code for the crashIfBadTarget link, right above). However, JavaDoc Link Codes in the first sentence are also displayed in indirect link pages (such as index pages). If no class prefix exists for the JavaDoc Link Code, then, although the link is valid in the class itself, it is not valid in the inderct link page. Therefore, if the crashIfBadTarget were found in the first sentence, it should have the class prefix: \~JD\~utiljdlc#crashIfBadTarget(jdf,slc,jdca,b)\~EJD\~.

Note: No matter what, functions outside of the JavaDoc Link Code are not validated. So although this link (url equals \~JD\~utiljdlc\~EJD\~#nonExistingFunction()) is broken, since the function is outside the JavaDoc Link Code it's not checked for validity.

Remember also, that zero parameter functions are not allowed inside a JavaDoc Link Code, ever. This is validated by crashIfBadTarget but not the SplitLinkCode constructor (SplitLinkCode just checks formatting, so it assumes that a zero-parameter function is actually a zero-parameter constructor. It does not verify that the target exists, and therefore cannot distinguish between constructors and functions).

**/ public final void appendRelUrl(SplitLinkCode split_linkCode, StringOrBuffer str_orBfr, JDFile jdf_linkSource, JDCArray jdc_array, boolean b_crashIfBadTarget, boolean b_ignoreIndirectJDLCs) { throwAXIfNull(str_orBfr, "str_orBfr", "appendRelUrl"); throwAXIfNull(jdc_array, "jdc_array", "appendRelUrl"); if(b_crashIfBadTarget) { try { crashIfBadTarget(jdf_linkSource, split_linkCode, jdc_array, b_ignoreIndirectJDLCs); } catch(AssertException ax) { throwAX("appendRelUrl: Attempting to generate link for JavaDoc code '" + getOriginalCode(split_linkCode) + "', in source file '" + jdf_linkSource.getList(sPD) + "'..." + ax.toString()); } } //We've made it this far, so the link points to a truly- //existing target. String sClass = null; try { sClass = split_linkCode.getClassName(); } catch(NullPointerException npx) { throwAX("appendRelUrl: split_linkCode is null."); } if(sClass != null) { if(!jdc_array.doesExist(sClass)) { throwAX("appendRelUrl: split_linkCode has a class name abbreviation of '" + sClass + "', but this is not an abbreviation that exists in jdc_array."); } appendRelUrlToFile(str_orBfr, jdf_linkSource, jdc_array.getJDClass(sClass)); } String sFunction = split_linkCode.getCnstrFuncName(); if(sFunction == null) { return; } str_orBfr.append("#"); if(jdc_array.doesExist(sFunction)) { //The only way a function can be the name of a class, is when //we're calling the constructor. So we only need the very last //part of the fully qualified class name...the file name itself. JDFile jdf = jdc_array.getJDClass(sFunction); str_orBfr.append(jdf.getPart(jdf.getPartCount() - 1) + "("); } else { //This is an actual function. We must trust that it's legal. str_orBfr.append(sFunction + "("); } if(split_linkCode.getParameterCount() == 0) { str_orBfr.append(")"); return; } //There is at least one parameter. for(int i = 0; i < split_linkCode.getParameterCount(); i++) { if(!jdc_array.doesExist(split_linkCode.getParamName(i))) { throwAX("appendRelUrl: Parameter abbreviation at array index " + i + " ('" + split_linkCode.getParamName(i) + "') does not exist in jdc_array."); } str_orBfr.append(jdc_array.getJDClass(split_linkCode.getParamName(i)).getList(sPD)); if(split_linkCode.isParamArray(i)) { str_orBfr.append("[]"); } if(i < (split_linkCode.getParameterCount() - 1)) { str_orBfr.append(", "); } } str_orBfr.append(")"); } /**

Get the original code, exactly as passed into the SplitLinkCode constructor. See the SplitLinkCode constructor.

**/ public final String getOriginalCode(SplitLinkCode split_linkCode) { StringBuffer sbLink = new StringBuffer(sES); try { if(split_linkCode.getClassName() != null) { sbLink.append(split_linkCode.getClassName()); if(split_linkCode.getCnstrFuncName() != null) { sbLink.append("#"); } } } catch(NullPointerException npx) { throwAX("getOriginalCode: split_linkCode is null."); } if(split_linkCode.getCnstrFuncName() != null) { sbLink.append(split_linkCode.getCnstrFuncName()); sbLink.append("("); } if(split_linkCode.getParameterCount() > 0) { for(int i = 0; i < split_linkCode.getParameterCount(); i++) { sbLink.append(split_linkCode.getParamName(i)); if(split_linkCode.isParamArray(i)) { sbLink.append("[]"); } if(i < (split_linkCode.getParameterCount() - 1)) { sbLink.append(","); } } } if(split_linkCode.getCnstrFuncName() != null) { sbLink.append(")"); } return sbLink.toString(); } //Used only by reportJDLinkCodeErrors private String sPreviousFile = sES; private String sCurrentFile = sES; private String sPreviousFileSCLR = sES; private String sCurrentFileSCLR = sES; private static final String sLS = sLINE_SEP; private int iFiles = 0; private int iUniqueJDLCs = 0; private int iAbsoluteJDLCs = 0; /**

This analyzes the source code existing in a directory, verifying all JavaDoc Link Codes, as well as links to source code.

This checks the basic syntax of all JavaDoc Link Codes existing in your code (via the SplitLinkCode constructor), as well as verifying that each abbreviation (class, function/constructor and parameter) within each JavaDoc Link Code actually exists in your class map configuration text file (via SplitLinkCode.getRelUrl).

Also note that this analyzes the JavaDoc existing directly in your java code (and package.html-s), and not the generated JavaDoc files. So if you have "sandbox" and "build" versions of your code, the sandbox code is analyzed, but the targets are expected to exist in the build-version of the source-code directory.

Parameters

Param name Description Notes
s_docletClassMapFile The path to the DocletClassMap  
s_dirToAnalyze All *.java and *.html files in this directory will be analyzed. Must end with a File.separator. For example, say you want to analyze all XBN Java code, and the source code exists in 'C:\\xbnjava\\xbn'. This should equal 'C:\\xbnjava\\xbn\\' and s_pkgPrefix should equal null. Alternatively, if you only want to analyze xbn.jdlcode, then this should equal 'C:\\xbnjava\\xbn\\jdlcode\\' and s_pkgPrefix should equal 'xbn'
b_analyzeJDLCodes If true, then JavaDoc Link Codes are analyzed for syntax errors. If false, JavaDoc link codes are not analyzed. When false, both b_cibJDLCTarget and b_ignoreIndirectJDLCs are ignored. Also note that you must analyze either JavaDoc Link Codes or source code links, or both. So if this is false, s_sourceCodeBaseDir must be non-null. If s_sourceCodeBaseDir is null, this must equal true.
s_sourceCodeBaseDir If non-null, then source code links are analyzed to ensure they point to existing files. If null, source code links are not analyzed. See Notes for b_analyzeJDLCodes. In addition, when this is non-null, s_relUrlJDToCodeBases is required, and b_listAllSCLs is not ignored. When this is null, s_relUrlJDToCodeBases must also be null, and b_listAllSCLs is ignored.
s_relUrlJDToCodeBases The relative directory (including the final url slash) between the JavaDoc and source-code base directories. Say your JavaDoc root directory is C:\\\\xbnjava\\\\documentation\\\\javadoc\\\\, and the source code root directory is C:\\\\xbnjava\\\\CODE\\\\. In this case, the relative url between these two directories is ../../CODE/.
b_listAllSCLs If true, then all source code links (regardless if they're valid or not) are printed out in the report, before any potential errors (errors in either source code links or JavaDoc Link Codes) are printed. See Notes for s_sourceCodeBaseDirp.
b_cibJDLCTarget Passed directly to getRelUrl, as b_crashIfBadTarget. See Notes for b_analyzeJDLCodes.
b_printXOnTheFly Should exception messages be printed during the analysis? If true, print exception messages both during analysis and in the report. If false, only in the report.  
s_pkgPrefix The package name prefix, when s_dirToAnalyze points to a sub-package directory. See Notes for s_sourceCodeBaseDir.
ow_output The OWriter to print output to. May not be null.
**/ public final void reportJDLinkCodeErrors(String s_docletClassMapFile, String s_dirToAnalyze, boolean b_analyzeJDLCodes, String s_sourceCodeBaseDir, String s_relUrlJDToCodeBases, boolean b_listAllSCLs, boolean b_cibJDLCTarget, boolean b_printXOnTheFly, String s_pkgPrefix, OWriter ow_output) { throwAXIfBadStr(s_docletClassMapFile, "s_docletClassMapFile", "reportJDLinkCodeErrors"); throwAXIfBadStr(s_dirToAnalyze, "s_dirToAnalyze", "reportJDLinkCodeErrors"); throwAXIfNull(ow_output, "ow_output", "reportJDLinkCodeErrors"); if(!ow_output.isActive()) { throwAX("reportJDLinkCodeErrors: ow_output is not active. ow_output.isActive() is false."); } if(!b_analyzeJDLCodes && s_sourceCodeBaseDir == null) { throwAX("reportJDLinkCodeErrors: b_analyzeJDLCodes equals false, and s_sourceCodeBaseDir is null. Nothing to analyze."); } if(s_sourceCodeBaseDir != null) { try { if(!s_sourceCodeBaseDir.endsWith(File.separator) || !s_relUrlJDToCodeBases.endsWith(sSLASH)) { throwAX("reportJDLinkCodeErrors: s_sourceCodeBaseDir ('" + s_sourceCodeBaseDir + "') is not null, therefore it must end with File.separator ('" + File.separator + "') and s_relUrlJDToCodeBases ('" + s_relUrlJDToCodeBases + "') must end with a url slash ('/')."); } } catch(NullPointerException npx) { throwAX("reportJDLinkCodeErrors: s_sourceCodeBaseDir ('" + s_sourceCodeBaseDir + "') is not null, but s_relUrlJDToCodeBases is."); } } class FFJavaAndSummary implements FileFilter { public boolean accept(File f_ile) { if(f_ile.isDirectory() && !f_ile.getName().toLowerCase().startsWith("cvs") && !f_ile.getName().toLowerCase().endsWith("cvs")) { return true; } if(f_ile.getPath().toLowerCase().endsWith(".java") || f_ile.getPath().toLowerCase().endsWith(sDOT_HTML)) { return true; } return false; } } Outputter optr = new Outputter(ow_output, new OConfig(true)); final String sDASHES = "----------------------"; optr.write(sLS + sLS + sDASHES + sLS + "LOADING CODE FILES..." + sLS + sDASHES + sLS); optr.write(sLS + sDASHES); optr.getOConfig().getMsgFormat().setIndent("\t"); DirFile df = new DirFile(s_dirToAnalyze, -1, (new FFJavaAndSummary()), optr); optr.getOConfig().getMsgFormat().setIndent(null); optr.write("...SUCCESS" + sLS + sDASHES + sLS + "STARTING ANALYSIS..." + sLS + sDASHES + sLS); JDCArray jdca = null; if(b_analyzeJDLCodes) { optr.write("Loading doclet class map: '" + s_docletClassMapFile + "'..."); jdca = getJDCAFromMapFile(s_docletClassMapFile); optr.write("...SUCCESS"); } StringBuffer sbSCLReport = null; if(b_listAllSCLs) { sbSCLReport = new StringBuffer(sES); } optr.write(" df.toString()=" + df.toString()); StringBuffer sbErrors = new StringBuffer(sES); VWString vws = new VWString(); optr.getOConfig().getMsgFormat().setIndent("\t"); int iErrors = actuallyReport(df, b_printXOnTheFly, sbErrors, jdca, b_cibJDLCTarget, false, optr, vws, s_pkgPrefix, s_sourceCodeBaseDir, s_relUrlJDToCodeBases, sbSCLReport); optr.getOConfig().getMsgFormat().setIndent(null); if(sbSCLReport != null && sbSCLReport.length() > 0) { optr.write(sLS + sLS + "---------------- START REPORT: All source-code links ----------------" + sLS + sLS + sbSCLReport.toString() + sLS + sLS + "---------------- END REPORT: All source-code links ----------------"); } if(sbErrors.length() > 0) { optr.write(sLS + sLS + "---------------- START REPORT: Javadoc Link Errors ----------------" + sLS + sLS + sbErrors.toString() + sLS + sLS + "---------------- END REPORT: Javadoc Link Errors ----------------" + sLS + sLS + "STATISTICS:" + sLS + " Unique JavaDoc Link Codes: " + iUniqueJDLCs + sLS + " Absolute JavaDoc Link Codes: " + iAbsoluteJDLCs + sLS + " Total errors: " + iErrors + sLS + " Total files analyzed: " + iFiles + sLS + " Average errors per file: " + ((new Double(iErrors)).doubleValue()/(new Double(iFiles)).doubleValue()) + sLS + sLS); } else { optr.write(sLS + sLS + sLS + " JavaDoc Link Codes: " + iUniqueJDLCs + " unique, " + iAbsoluteJDLCs + " absolute." + sLS + " NO ERRORS. Yay. :' )" + sLS + sLS + sLS); } } private int actuallyReport(DirFile dir_file, boolean b_printXOnTheFly, StringBuffer sb_errors, JDCArray jdc_array, boolean b_cibJDLCTarget, boolean b_ignoreIndirectJDLCs, Outputter optr_dbg, VWString acs_uniqueGapNames, String s_pkgPrefix, String s_sourceCodeBaseDir, String s_relUrlJDToCodeBases, StringBuffer sb_sclReport) { final String sERR_2CHOPUP2 = getXMsgPrefix() + sUD + "crashIfBadTarget: "; int iErrors = 0; String sDebugPfx = " " + (new UtilString()).getDuped(" ", dir_file.getLevelsBelowBaseDir()); iFiles += dir_file.getCountSubFiles(); for(int i = 0; i < dir_file.getCountSubFiles(); i++) { File fThis = dir_file.getSubFile(i).getFileObjectThis(); sCurrentFile = fThis.getName().substring(0, fThis.getName().lastIndexOf(sPD)); sCurrentFileSCLR = sCurrentFile; StringBuffer sbLinkSource = new StringBuffer(dir_file.getSubFile(i).getRelDir()); if(s_pkgPrefix != null) { sbLinkSource.insert(0, s_pkgPrefix); } JDFile jdfLinkSource = new JDFile(sbLinkSource.toString(), sCurrentFile + sDOT_HTML); optr_dbg.writeNoln(true, sDebugPfx + sCurrentFile + " "); int j = 0; String sPath = fThis.getPath(); FLRFile flrf = new FLRFile(sPath); ForLineRetrieval flrJDLC = null; ForLineRetrieval flrSCL = null; if(sPath.endsWith(".java")) { StringBuffer sbJDText = getJDTextFromJava(flrf); if(jdc_array != null) { flrJDLC = new FLRString(sbJDText); } if(s_relUrlJDToCodeBases != null) { //Must duplicate sbJDText!!! flrSCL = new FLRString(new StringBuffer(sbJDText.toString())); } } else { if(jdc_array != null) { flrJDLC = flrf; } if(s_relUrlJDToCodeBases != null) { if(jdc_array == null) { flrSCL = new FLRFile(sPath); } else { flrSCL = flrf; } } } final String sJVDC_LNK = "-- JAVADOC LINK: "; if(jdc_array != null) { //Analyze Javadoc Link Codes. try { i_i ii = uTemplate.addUnqGapNamesToACS(acs_uniqueGapNames, flrJDLC, new GapConfig(getJDLinkGapTextStart(), getJDLinkGapTextEnd()), (new Outputter())); iUniqueJDLCs += ii.i1; iAbsoluteJDLCs += ii.i2; } catch(AssertException ax) { String sXMsg = ax.toString().substring(ax.toString().indexOf(sERR_2CHOPUP2) + sERR_2CHOPUP2.length()); iErrors++; optr_dbg.writeNoln("x"); if(b_printXOnTheFly) { optr_dbg.write(sLS + sJVDC_LNK + acs_uniqueGapNames.getString(j) + sLS + sXMsg); } if(!sPreviousFile.equals(sCurrentFile)) { sb_errors.append(sLS + "SOURCE FILE: " + fThis.getPath() + sLS); } sPreviousFile = sCurrentFile; if(acs_uniqueGapNames.size() > 0) { sb_errors.append(sJVDC_LNK + acs_uniqueGapNames.getString(j) + sLS); } sb_errors.append(sXMsg + sLS); } for(; j < acs_uniqueGapNames.size(); j++) { try { SplitLinkCode slc = new SplitLinkCode(acs_uniqueGapNames.getString(j)); getRelUrl(slc, jdfLinkSource, jdc_array, b_cibJDLCTarget, b_ignoreIndirectJDLCs); optr_dbg.writeNoln(sPD); } catch(AssertException ax) { String sXMsg = ax.toString().substring(ax.toString().indexOf(sERR_2CHOPUP2) + sERR_2CHOPUP2.length()); iErrors++; optr_dbg.writeNoln("x"); if(b_printXOnTheFly) { optr_dbg.write(sLS + sJVDC_LNK + acs_uniqueGapNames.getString(j) + sLS + sXMsg); } if(!sPreviousFile.equals(sCurrentFile)) { sb_errors.append(sLS + "SOURCE FILE: " + fThis.getPath() + sLS); } sPreviousFile = sCurrentFile; if(acs_uniqueGapNames.size() > 0) { sb_errors.append(sJVDC_LNK + acs_uniqueGapNames.getString(j) + sLS); } sb_errors.append(sXMsg + sLS); } } acs_uniqueGapNames.initializeVector(); } if(jdc_array != null && s_sourceCodeBaseDir != null) { optr_dbg.writeNoln("|"); } if(s_sourceCodeBaseDir != null) { //Analyze source-code links. while(flrSCL.hasMoreLines()) { SOBStringBuffer ssb = new SOBStringBuffer(flrSCL.getNextLine()); int iDeletedChars = 0; int iLink = 0; while(iLink != -1) { iLink = ssb.indexOf(s_relUrlJDToCodeBases); if(iLink == -1) { continue; } String sOrigLink = uSOB.getStringAround(ssb, iLink, "\"", "\"", false, false); SOBStringBuffer ssbLink = new SOBStringBuffer(sOrigLink); ssb.delete(0, uSOB.getGSAInfo().i4); iDeletedChars += uSOB.getGSAInfo().i4; //Delete as many "../" as there are directory //parts in jdf_linkSource. try { int iDirParts = jdfLinkSource.getPartCount() - 1; while(iDirParts > 0) { int iFound = (jdfLinkSource.getPartCount() - 1 - iDirParts); if(ssbLink.startsWith(s_relUrlJDToCodeBases) || !ssbLink.startsWith("../")) { throwAX("It is expected that the source-code link starts with " + (jdfLinkSource.getPartCount() - 1) + " instances of '../', followed by s_relUrlJDToCodeBases ('" + s_relUrlJDToCodeBases + "'). " + iFound + " initial '../' " + (iFound==1?"was":"were") + " actually found."); } ssbLink.delete(0, 3); iDirParts--; } //Now the relative link from the javadoc and //source-code base directories must exist at //the beginning of what remains. if(!ssbLink.startsWith(s_relUrlJDToCodeBases)) { int iFound = jdfLinkSource.getPartCount() - 1; while(ssbLink.startsWith("../") && !ssbLink.startsWith(s_relUrlJDToCodeBases)) { iFound++; ssbLink.delete(0, 3); } if(iFound > (jdfLinkSource.getPartCount() - 1)) { throwAX("It is expected that the source-code link starts with " + (jdfLinkSource.getPartCount() - 1) + " instances of '../', followed by s_relUrlJDToCodeBases ('" + s_relUrlJDToCodeBases + "'). " + iFound + " initial '../' " + (iFound==1?"was":"were") + " actually found."); } throwAX("It is expected that the source-code link starts with " + (jdfLinkSource.getPartCount() - 1) + " instances of '../', followed by s_relUrlJDToCodeBases ('" + s_relUrlJDToCodeBases + "'). The correct amount of '../' were found, but s_relUrlJDToCodeBases does not exist after them."); } //It does exist. Delete it. ssbLink.delete(0, s_relUrlJDToCodeBases.length()); //Now, what remains must be a file that //actually exists within s_sourceCodeBaseDir. if(sb_sclReport != null) { if(!sPreviousFileSCLR.equals(sCurrentFileSCLR)) { sb_sclReport.append(sLS + jdfLinkSource.getList(sPD) + sLS); } sPreviousFileSCLR = sCurrentFileSCLR; sb_sclReport.append(" " + ssbLink.toString() + sLS); } if(!sSLASH.equals(File.separator)) { uSOB.replaceAll(ssbLink, sSLASH, File.separator); } File f = new File(s_sourceCodeBaseDir + ssbLink.toString()); if(!f.exists()) { throwAX("The target does not exist: '" + f.getPath() + "'."); } optr_dbg.writeNoln(sPD); } catch(AssertException ax) { optr_dbg.writeNoln("x"); String sError = "-- SOURCE CODE LINK: " + sOrigLink + sLS + ax.toString().substring(ax.toString().lastIndexOf(getXMsgPrefix() + sUD) + (getXMsgPrefix() + sUD).length()); iErrors++; if(b_printXOnTheFly) { optr_dbg.write(sError); } if(!sPreviousFile.equals(sCurrentFile)) { sb_errors.append(sLS + "SOURCE FILE: " + fThis.getPath() + sLS); } sPreviousFile = sCurrentFile; sb_errors.append(sError + sLS); } } } } optr_dbg.newln(); } for(int i = 0; i < dir_file.getCountSubDirs(); i++) { optr_dbg.write(sDebugPfx + dir_file.getSubDir(i).getFileObjectThis().getName()); iErrors += actuallyReport(dir_file.getSubDir(i), b_printXOnTheFly, sb_errors, jdc_array, b_cibJDLCTarget, b_ignoreIndirectJDLCs, optr_dbg, acs_uniqueGapNames, s_pkgPrefix, s_sourceCodeBaseDir, s_relUrlJDToCodeBases, sb_sclReport); } return iErrors; } private void appendLinkFromCode(StringBuffer str_buffer, String s_jdLink, JDFile jdf_current, JDCArray jdc_array, boolean b_cibJDLCTarget, boolean b_ignoreIndirectJDLCs) { SplitLinkCode slc = null; try { slc = new SplitLinkCode(s_jdLink); } catch(AssertException ax) { throwAX("appendLinkFromCode: Error while attempting to parse the JavaDoc Link Code: '" + s_jdLink + "': " + ax.toString()); } appendRelUrl(slc, str_buffer, jdf_current, jdc_array, b_cibJDLCTarget, b_ignoreIndirectJDLCs); } private void throwAX(String s_callingFunc, int i_lineNumber, String s_message) { throwAX(s_callingFunc + " [line=" + i_lineNumber + "]: " + s_message); } } class ClassStuff extends XBNObject { Class cThis = null; Constructor[] aConstructors = null; Method[] aMethods = null; public ClassStuff(Class c_lass) { Constructor[] aCnstr = c_lass.getDeclaredConstructors(); int iPriv = getPrivateCount(aCnstr); aConstructors = new Constructor[aCnstr.length - iPriv]; int j = 0; for(int i = 0; i < aConstructors.length; i++) { while(aCnstr[j] == null) { j++; } aConstructors[i] = aCnstr[j]; j++; } Method[] am = c_lass.getDeclaredMethods(); iPriv = getPrivateCount(am); aMethods = new Method[am.length - iPriv]; j = 0; for(int i = 0; i < aMethods.length; i++) { while(am[j] == null) { j++; } aMethods[i] = am[j]; j++; } cThis = c_lass; } private int getPrivateCount(Member[] a_member) { int iPriv = 0; for(int i = 0; i < a_member.length; i++) { if(Modifier.isPrivate(a_member[i].getModifiers())) { a_member[i] = null; iPriv++; } } return iPriv; } } class Classes extends XBNObject { private BinarySearchData bsd = null; private Vector v = new Vector(100, 10); public Classes() { } public final void add(Class c_lass) { throwAXIfNull(c_lass, "c_lass", "add"); if(bsd == null) { //This is the first element. bsd = new BinarySearchData(1, true); v.add(new ClassStuff(c_lass)); return; } //There is the second or subsequent element. Insert it at //the proper place. bsd.reset(); int iClosest = -1; while(bsd.getIdxMiddle() != -1) { iClosest = bsd.getIdxMiddle(); String sMiddle = ((ClassStuff)v.elementAt(bsd.getIdxMiddle())).cThis.getName(); int iCmpr = c_lass.getName().compareTo(sMiddle); if(iCmpr == 0) { throwAX("add: c_lass '" + c_lass.getName() + "' already exists."); } bsd.prepareForNextSearch(iCmpr < 0); } v.insertElementAt(new ClassStuff(c_lass), iClosest); } public final ClassStuff getClassStuff(Class c_lass) { int iIdx = -1; try { iIdx = indexOf(c_lass.getName()); } catch(NullPointerException npx) { throwAX("getClassStuff: c_lass is null."); } if(iIdx == -1) { throwAX("add: c_lass '" + c_lass.getName() + "' does not exist."); } return (ClassStuff)v.elementAt(iIdx); } public final ClassStuff getClassStuff(String s_fqClassName) { throwAXIfBadStr(s_fqClassName, "s_fqClassName", "getClassStuff"); int iIdx = indexOf(s_fqClassName); if(iIdx == -1) { throwAX("add: Class s_fqClassName ('" + s_fqClassName + "') does not exist."); } return (ClassStuff)v.elementAt(iIdx); } public final boolean doesExist(String s_fqClassName) { if(bsd == null) { //There are no elements yet. return false; } //There is at least one element. int iIdx = indexOf(s_fqClassName); return (iIdx != -1); } public final int indexOf(String s_fqClassName) { bsd.reset(); while(bsd.getIdxMiddle() != -1) { String sMiddle = ((ClassStuff)v.elementAt(bsd.getIdxMiddle())).cThis.getName(); int iCmpr = s_fqClassName.compareTo(sMiddle); if(iCmpr == 0) { return bsd.getIdxMiddle(); } bsd.prepareForNextSearch(iCmpr < 0); } return -1; } }