/* 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.array.APString; import xbn.array.BinarySearchData; import xbn.array.primitive.PASString; import java.util.StringTokenizer; import java.io.File; /**

Represents a single JavaDoc (html) file that may or may not contain JavaDoc Link Codes.

JDFiles can have JavaDoc Link Codes directly, indirectly or not at all.

Source code:  JDFile.java.

@version 0.9b @author Jeff Epstein, http://sourceforge.net/projects/xbnjava. **/ public class JDFile extends XBNObject { //ENTIRE strings used four or more times. private static final String sPD = "."; private static final String[] asNO_LINKS = new String[] {"allclasses-frame", "help-doc", "index", "overview-frame", "overview-tree", "package-frame", "package-list", "package-tree", "packages", "serialized-form"}; private static final String[] asLINKS_INDIRECT = new String[] {"deprecated-list", "index-1", "index-10", "index-11", "index-12", "index-13", "index-14", "index-15", "index-16", "index-17", "index-18", "index-19", "index-2", "index-20", "index-21", "index-22", "index-23", "index-24", "index-25", "index-26", "index-3", "index-4", "index-5", "index-6", "index-7", "index-8", "index-9"}; private final APString apsNO_LINKS = new APString(asNO_LINKS); private final APString apsLINKS_INDIRECT = new APString(asLINKS_INDIRECT); private final BinarySearchData bsdNO_LINKS = new BinarySearchData(apsNO_LINKS.getLength(), true); private final BinarySearchData bsdLINKS_INDIRECT = new BinarySearchData(apsLINKS_INDIRECT.getLength(), true); private String[] asFile = null; private PASString pass = null; private PASString passPkgOnly = null; private boolean bLnksDrct = false; private boolean bLnksIndrct = false; /**

Create a JDFile from the path/filename combination found in HTMLStandardWriter. It is expected that this constructor only be called by HTMLStandardWriter, via the "javadoc" process.

@param s_relDir The relative url directory to the class or html file. For example, if xbn.jdlcode.UtilJDLCode is a class in your class map configuration text file, then this variable should equal "xbn/jdlcode". Another example: The file c:\\home\\my_javadoc\\index-files\\index-1.html has a relative url directory of index-files @param s_fileName The name of the class or html file. For example, if xbn.jdlcode.UtilJDLCode is in your class map, then this variable should equal "UtilJDLCode". Another example: The file c:\\home\\my_javadoc\\index-files\\index-1.html has a file name of index-1.html **/ public JDFile(String s_relDir, String s_fileName) { throwAXIfNull(s_relDir, "s_relDir", sCNSTR); throwAXIfBadStr(s_fileName, "s_fileName", sCNSTR); boolean bEndsInHtml = s_fileName.endsWith(".html"); if(!s_fileName.equals("package-list") && !bEndsInHtml && !s_fileName.equals("stylesheet.css")) { throwAX("constructor: It is expected that this constructor be called directly from HtmlStandardWriter. If it were, then s_fileName would either equal 'package-list', 'stylesheet.css' or end with '.html'. Currently '" + s_fileName + "'. Extra information...s_relDir='" + s_relDir + "'."); } if(bEndsInHtml) { //This ends with ".html". Delete this postfix. //".html".length() = 5 s_fileName = s_fileName.substring(0, (s_fileName.length() - 5)); } //Split the directory name into an array. if(s_relDir.length() < 1) { asFile = new String[] {s_fileName}; } else { StringTokenizer st = new StringTokenizer(s_relDir, File.separator); createPartArray(st, s_fileName); } //Should this be locked? pass = new PASString(asFile, false); defineLinkState(getFileName()); } /**

Create a JDFile from the fully qualified class name, as found in the class map configuration file. @param s_fqClassName The fully qualified name to a JavaDoc html file, divided by dots, minus the ".html". For example: xbn.array.VWObject or index-files.index-1. Must...

**/ public JDFile(String s_fqClassName) { throwAXIfBadStr(s_fqClassName, "s_fqClassName", sCNSTR); if(s_fqClassName.startsWith(sPD) || s_fqClassName.endsWith(sPD)) { throwAX("constructor: s_fqClassName ('" + s_fqClassName + "') must not start or end with a period ('.')."); } if(s_fqClassName.endsWith(".html")) { throwAX("constructor: s_fqClassName ('" + s_fqClassName + "') must not end with '.html'."); } if(s_fqClassName.indexOf("..") != -1) { throwAX("constructor: '..' found in s_fqClassName ('" + s_fqClassName + "') at index " + s_fqClassName.indexOf("..") + sPD); } if(s_fqClassName.length() > 100) { throwAX("constructor: The length of s_fqClassName ('" + s_fqClassName + "') must have between 1 and 100 characters, inclusive. Currently " + s_fqClassName.length() + sPD); } StringTokenizer st = new StringTokenizer(s_fqClassName, sPD); createPartArray(st, null); //Why is this not locked? pass = new PASString(asFile, false); defineLinkState(getFileName()); } /**

How many parts exist in the name of this JDClass?

Each part is divided by a period. For example, xbn.XBNObject has two parts, and xbn.jdlcode.JDFile has three.

**/ public final int getPartCount() { return asFile.length; } /**

How many directories are there in the file represented by this JDFile?

@return (getPartCount() - 1) **/ public final int getDirCount() { return (getPartCount() - 1); } /**

Get the part at the requested array index.

@param i_dx The array index for the (private, internal) string array of parts. Must range zero..(getPartCount() - 1). **/ public final String getPart(int i_dx) { try { return pass.getString(i_dx); } catch(ArrayIndexOutOfBoundsException aioobx) { throwAX("getPart: i_dx (" + i_dx + ") is invalid. getPartCount()" + getPartCount() + sPD); } //Never reached. Needed for compile. return null; } /**

Get the name of this JDFile.

@return getPart(getPartCount() - 1) **/ public final String getFileName() { return getPart(getPartCount() - 1); } /**

Get an PASString representing the part names. This PASString object is created once, at constructor time.

**/ public PASString getPASString() { return pass; } /**

Get an PASString representing all the part names, except the final part (the file name itself).

This PASString object is created once, when this function is first called.

**/ public PASString getPASSPackage() { if(passPkgOnly == null) { String[] asPkgOnly = new String[getDirCount()]; for(int i = 0; i < asPkgOnly.length; i++) { asPkgOnly[i] = pass.getString(i); } passPkgOnly = new PASString(asPkgOnly, true); } return passPkgOnly; } /**

Get a list of all part names, with the requested divider string.

@param s_divider The dividing string. May not be null. @return Assuming this array of parts: {"xbn", "jdlcode", "JDFile"}, if s_divider equals...

Specifically, this returns getPASString().getList(s_divider)

**/ public String getList(String s_divider) { return getPASString().getList(s_divider); } /**

Get a list of the package name for this JDFile (all parts but the last, which is the file name).

@param s_divider The dividing string. May not be null. @return Assuming this array of parts: {"xbn", "jdlcode", "JDFile"}, if s_divider equals...

Specifically, this returns getPASSPackage().getList(s_divider)

**/ public String getPackageList(String s_divider) { return getPASSPackage().getList(s_divider); } /**

Get the prefix for this JDFile.

@return "" If getDirCount equals zero.
getPart(0) If otherwise. **/ public String getPackagePrefix() { if(getDirCount() == 0) { return sES; } return getPart(0); } /**

Is the provided JDFile in the same package as this JDFile?.

@param jd_file The JDFile, whose package should be declared to this JDFile's package. May not be null. @return jd_file.getPackageList(".").equals(getPackageList(".")) **/ public boolean isInSamePackageAs(JDFile jd_file) { try { return jd_file.getPackageList(sPD).equals(getPackageList(sPD)); } catch(NullPointerException npx) { throwAX("isInSamePkg: jd_file is null."); } //Never reached. Required for compile. return false; } /**

Is this JDFile local?

See isPrimitive and isExternal.

What does local mean?

Let's say you generate your JavaDoc into C:\\my_java_code\\javadoc\\, and the following three directories--after running the JavaDoc command--exist in it: "index-files", "xbn", "random_java_pkg". index-files is a directory created automatically by JavaDoc. xbn and random_java_pkg are the directories in which the documentation for my packages are held. So the xbn.* and random_java_pkg.* packages are local.

@return true All JDFiles are local. **/ public boolean isLocal() { return true; } /**

Is this JDFile primitive?

See isLocal and isExternal.

What does primitive mean?

int, char, long, float, ...

For the sake of Javadoc, primitive variables cannot be linked to, nor from. There are no JavaDoc html pages existing for these primitive types.

@return false All JDFiles are local. **/ public boolean isPrimitive() { return false; } /**

Is this JDFile external?

See isLocal and isPrimitive.

What does external mean?

All packages that are not local, but are defined in the class map configuration text file--even if you never link to that package in your documentation.

@return false All JDFiles are local. **/ public boolean isExternal() { return false; } /**

Get some information about this JDFile.

**/ public String toString() { return getList(sPD); } /**

Does this JDFile contain no JavaDoc Link Codes?

If the name of this file exists in getAPSLinkPagesNONE, then this is a file that is not editable at all by you (the Java programmer). Rather, this file is created solely by the javadoc process. Therefore, this page cannot contain nor display any JavaDoc Link Codes, either directly or indirectly.

JDFiles with no JavaDoc Link Codes cannot be analyzed by UtilJDLCode.crashIfBadTarget.

@return (!hasJDLCsDirectly() && !hasJDLCsIndirectly()) **/ public final boolean hasNoJDLCodes() { return (!hasJDLCsDirectly() && !hasJDLCsIndirectly()); } /**

Is it possible for this JDFile to contain JavaDoc Link Codes "directly"?

If the name of this file does not exist in either getAPSLinkPagesNONE or getAPSLinkPagesINDIRECT, then this JDFile represents one of the actual "*.java" or "package.html" files in your packages. It therefore can contain JavaDoc Link Codes in it.

For example

This link was written by me in JDFile.java, has this JavaDoc Link Code as the url:

\~JD\~vwo\~EJD\~

It is displayed in this file (JDFile.html), "directly", with the resulting relative url:

~JD~vwo~EJD~

Direct and Indirect

JDFiles that have JavaDoc Link Codes directly can also display them indirectly. For example, Template.java has no documentation written in it for getName(). However, Template.html inherits the documentation for this function from Named.getName. Note that Template.html#getName says "Description copied from...".

Technically speaking: When [yourJDFile].hasJDLCsDirectly() equals true, [yourJDFile].hasJDLCsIndirectly() also (always) equals true.

UtilJDLCode.crashIfBadTarget()

You can ensure that indirect JavaDoc Link Codes are pointing to a target that actually exists, by using UtilJDLCode.crashIfBadTarget. This function is optionally called by the javadoc and ReportBadJDLinkCodes processes.

The SplitLinkCode constructor also validates JavaDoc Link Codes, but only to ensure proper formatting.

Process How to turn target validation on How to turn it off
javadoc Go into HtmlStandardWriter.java, change the second-to-last boolean (the third-to-last overall) parameter of "getJavadocLine" to true, and recompile (sorry, no convenient method at this point..). Change the second-to-last boolean parameter of "getJavadocLine" to false and recompile.
ReportBadJDLinkCodes When you run ReportBadJDLinkCodes, set the third command-line parameter ("ENSURE_LINK_TARGETS_EXIST") to "true". Set the third command-line parameter to "false".

Validating direct JavaDoc Link Codes has precedence over indirect. When direct ones are validated (via either process, or the crashIfBadTarget function) indirect ones may be validated. When direct ones are not being validated, it is not possible to validate indirect ones.

@return true If it is possible for the html file (as represented by this JDFile) to have JavaDoc Link Codes directly.
false If it is not possible. **/ public final boolean hasJDLCsDirectly() { return bLnksDrct; } /**

Is it possible for this JDFile contain JavaDoc Link Codes "indirectly" (Examples: good and bad)?

If the name of this file exists in getAPSLinkPagesINDIRECT, then this is a file that is both...

For example

The "good" and "bad" example links above were written in JDFile.java, having these urls:

Good: \~JD\~jdf#getList(s)\~EJD\~
Bad: \~JD\~getList(s)\~EJD\~

These are displayed in this file (JDFile.html), "directly", with the urls:

Good: ~JD~jdf#getList(s)~EJD~
Bad: #getList(java.lang.String)

These same links are also displayed "indirectly" in the "H" index page, with these urls:

Good: ../xbn/jdlcode/JDFile.html#getList(java.lang.String)
Bad: #getList(java.lang.String)

The "good" link works in both JDFile.html and the "H" index page. The "bad" one works in JDFile (this class, this function's first sentence of documentation), but not the "H" index.

All JavaDoc Link Codes existing in the first sentence of a JavaDoc Block (as the "good" and "bad" links are in this) are also displayed in index and, potentially, deprecated pages. Therefore, to prevent errors such as demonstrated with the "bad" link, ensure that you provide the class prefix to every first-sentence JavaDoc Link Code.

Indirect and Direct

When [yourJDFile].hasJDLCsIndirectly() equals true, [yourJDFile].hasJDLCsDirectly() may equal true or false.

JDFiles that have JavaDoc Link Codes indirectly may or may not have them directly. For example, as described in the documentation for hasJDLCsDirectly, Template.html has both indirect and direct. However, all index pages have only indirect.

UtilJDLCode.crashIfBadTarget()

You can ensure that indirect JavaDoc Link Codes are pointing to a target that actually exists, by using UtilJDLCode.crashIfBadTarget. This function is optionally called by the javadoc process.

The SplitLinkCode constructor also validates JavaDoc Link Codes, but only to ensure proper formatting.

Since these JDFiles contain no JavaDoc Link Codes directly, you may or may not want the targets verified.

Process How to turn target validation on How to turn it off
javadoc Go into HtmlStandardWriter.java, change the last boolean (the second-to-last overall) parameter of "getJavadocLine" to false, and recompile (sorry, no convenient method at this point..). Change the last boolean parameter of "getJavadocLine" to true and recompile.
ReportBadJDLinkCodes N/A. Indirect JavaDoc Link Codes cannot be checked by this process  

This table assumes direct JavaDoc Link Codes are being validated.

Validating direct JavaDoc Link Codes has precedence over indirect. When direct ones are being validated (via either process) indirect ones may be validated. When direct ones are not being validated, it is not possible to validate indirect ones.

@return true If it is possible for the html file (as represented by this JDFile) to display JavaDoc Link Codes indirectly.
false If it is not possible. **/ public final boolean hasJDLCsIndirectly() { return bLnksIndrct; } /**

Get the names for all JavaDoc html files that have/display no JavaDoc Link Codes at all.

See hasNoJDLCodes.

@return new APString(new String[] {"allclasses-frame", "help-doc", "index", "overview-frame", "overview-tree", "package-frame", "package-list", "package-tree", "packages", "serialized-form"}

The APString is created only once.

**/ public final APString getAPSLinkPagesNONE() { return apsNO_LINKS; } /**

Get the names for all JavaDoc html files that only display JavaDoc Link Codes indirectly.

See hasJDLCsIndirectly.

@return new APString(new String[] {"deprecated-list", "index-1", "index-10", "index-11", "index-12", "index-13", "index-14", "index-15", "index-16", "index-17", "index-18", "index-19", "index-2", "index-20", "index-21", "index-22", "index-23", "index-24", "index-25", "index-26", "index-3", "index-4", "index-5", "index-6", "index-7", "index-8", "index-9"}

The APString is created only once.

**/ public final APString getAPSLinkPagesINDIRECT() { return apsLINKS_INDIRECT; } /**

Does the provided JDFile represent the same file as this JDFile?

@param jd_file The JDFile to compare this against. May not be null. @return true If jd_file and this...
false If otherwise. **/ public final boolean equals(JDFile jd_file) { try { if(jd_file.getPartCount() != getPartCount()) { return false; } } catch(NullPointerException npx) { throwAX("equals: jd_file is null."); } //The part counts are equal. for(int i = 0; i < getPartCount(); i++) { if(!getPart(i).equals(jd_file.getPart(i))) { return false; } } //All parts are equal! return true; } private void createPartArray(StringTokenizer st_okenizer, String s_fileName) { int iArrayLength = st_okenizer.countTokens(); if(s_fileName != null) { iArrayLength++; } asFile = new String[iArrayLength]; int i = 0; while(st_okenizer.hasMoreTokens()) { String sPart = st_okenizer.nextToken(); asFile[i++] = sPart; } if(s_fileName != null) { asFile[asFile.length - 1] = s_fileName; } } private final void defineLinkState(String s_pageName) { if(getBSDIdx(s_pageName, bsdNO_LINKS, asNO_LINKS)) { bLnksDrct = false; bLnksIndrct = false; } else if(getBSDIdx(s_pageName, bsdLINKS_INDIRECT, asLINKS_INDIRECT)) { bLnksIndrct = true; } else { bLnksDrct = true; } } private final boolean getBSDIdx(String s_pageName, BinarySearchData binary_searchData, String[] as_linkPageNames) { binary_searchData.reset(); while(binary_searchData.getIdxMiddle() != -1) { String sMiddle = as_linkPageNames[binary_searchData.getIdxMiddle()]; int iComparison = s_pageName.compareTo(sMiddle); if(iComparison == 0) { return true; } binary_searchData.prepareForNextSearch(iComparison < 0); } return false; } }