/* 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.util; import xbn.XBNObject; import xbn.array.VWObject; import xbn.array.VWString; import xbn.array.primitive.PASString; import xbn.array.primitive.PARString; import xbn.array.primitive.PARDupNullLen; import xbn.array.primitive.PAROrderDir; import xbn.output.Outputter; import xbn.placeholder.i_i_i; import xbn.placeholder.s_acs_acs; import xbn.string.FLRString; import xbn.string.SOBStringBuffer; import xbn.string.StringOrBuffer; import xbn.string.SOBString; import xbn.string.UtilSOB; import xbn.string.UtilString; import xbn.AssertException; import java.io.BufferedInputStream; import java.io.InputStream; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.net.URL; import java.util.Enumeration; import java.util.Hashtable; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; /**

Random functions.

Source code:  Utility.java

@version 0.9b @author Jeff Epstein, http://sourceforge.net/projects/xbnjava. **/ public class Utility extends XBNObject { //ENTIRE strings used four or more times. private UtilString uStr = new UtilString(); /**

Create a Utility. This constructor does nothing.

**/ public Utility() { } /**

A utility function for applications to get all of the actually-provided command line parameters.

This returns all values contained in the provided string array, space delimiting each. If any value contains spaces, then it is put inside of double quotes (unless it contains a double quote, in which case it is put inside single quotes).

If there are no parameters, the empty string ('') is returned.

**/ public static final StringBuffer getActlCmdLineParams(String[] as_cmdLineParams) { StringBuffer sb = new StringBuffer(""); if(as_cmdLineParams.length < 1) { return sb; } for(int i = 0; i < as_cmdLineParams.length; i++) { if(as_cmdLineParams[i].indexOf(" ") != -1) { //This value contains spaces, and was therefore //provided inside quotes. if(as_cmdLineParams[i].indexOf("\"") == -1) { sb.append("\"").append(as_cmdLineParams[i]).append("\""); } else { //The value of this parameter contains double //quotes. Therefore It must have been provided //inside *single* quotes. Print them out that //way. sb.append("'").append(as_cmdLineParams[i]).append("'"); } //(If it contains both single and double quotes, oh //well. This is not that big enough of a deal...) } else { //No spaces. sb.append(as_cmdLineParams[i]); } if(i < (as_cmdLineParams.length - 1)) { //There's at least one more parameter sb.append(" "); } } return sb; } /**

Get a random boolean.

@return (getRandomInt(0, 1) == 1) **/ public final boolean getRandomBoolean() { return (getRandomInt(0, 1) == 1); } /**

Get a random int.

@return getRandomInt(0, i_max) **/ public final int getRandomInt(int i_max) { return getRandomInt(0, i_max); } /**

Get a random int between the desired bounds.

@param i_min The minimum possible int to return. May not be greater than i_max. @param i_max The maximum possible int to return. May not be less than i_min. @return A random int between i_min and i_max. **/ public final int getRandomInt(int i_min, int i_max) { if(i_min > i_max) { throwAX("getRandomInt: i_min (" + i_min + ") must be less than or equal to i_max (" + i_max + ")."); } int i = ((new Double(Math.random() * 100000000)).intValue()); try { i = i % (i_max + 1 - i_min); } catch(java.lang.ArithmeticException arithx) { throwAX("getRandomInt: i_max (" + i_max + ") must not equal -1. " + arithx.toString()); } return (i + i_min); } /**

Get an optimized version of the provided Hashtable.

A Hashtable is only optimized as long as no other objects are added to it.

@param h_ashtable The Hashtable to optimize. May not be null and h_ashtable.size must be one or greater. @return A hashtable whose capacity is initialized to h_ashtable.size(), and it's "load factor" is set to 1, meaning "100%". That is, when a new object is added (causing the total number of objects to exceed [size * loadfactor]) capacity is added to the Hashtable, decreasing its efficiency. Therefore, if no more objects are added to this hashtable, it's capacity never changes and retrieving objects from it is as quick and efficient as possible. **/ public final Hashtable getOptimizedHT(Hashtable h_ashtable) { throwAXIfNull(h_ashtable, "h_ashtable", "getOptimizedHT"); if(h_ashtable.size() < 1) { throwAX("getOptimizedHT: h_ashtable.size() equals zero."); } Hashtable htOptimized = new Hashtable(h_ashtable.size(), 1); Enumeration e = h_ashtable.keys(); while(e.hasMoreElements()) { String sKey = (String)e.nextElement(); htOptimized.put(sKey, h_ashtable.get(sKey)); } return htOptimized; } /**

Read all text in from a web page, and append it onto a StringOrBuffer.

I got this from http://www.davidreilly.com/java/java_network_programming/, item 2.3.

Be aware that this is a slooooow function, sometimes. But since I've only ever tried it on dialup, take this warning with a grain of salt.

**/ public final void appendWebPage(StringOrBuffer str_orBfr, String s_httpUrl, char[] ac_endingString) throws MalformedURLException, IOException { throwAXIfBadStr(s_httpUrl, "s_httpUrl", "appendWebPage"); if(ac_endingString != null && ac_endingString.length < 1) { throwAX("appendWebPage: ac_endingString is non-null and zero elements in length."); } // Create an URL instance URL url = new URL(s_httpUrl); // Get an input stream for reading InputStream is = url.openStream(); // Create a buffered input stream for efficency BufferedInputStream bis = new BufferedInputStream(is); int iEndStringIdx = 0; // Repeat until end of file while(true) { int iChar = bis.read(); // Check for EOF if (iChar == -1) { break; } char c = (char)iChar; try { str_orBfr.append(c); } catch(NullPointerException npx) { throwAX("appendWebPage: str_orBfr is null."); } if(ac_endingString != null) { //There is an ending string; if(c == ac_endingString[iEndStringIdx]) { //The character just retrieved is equal to the //next character in the ending string. if(iEndStringIdx == (ac_endingString.length - 1)) { //The entire string has been found. Done. return; } iEndStringIdx++; } else { iEndStringIdx = 0; } } } if(ac_endingString != null) { //Should have exited at the "return" above. throwAX("appendWebPage: ac_endingString ('" + (new String(ac_endingString)) + "') is not null, but is not found in the downloaded text."); } } /**

Read all text in from a file and append it onto a StringOrBuffer. No parsing is done, this is just a blind file read. Note: This function is useful, not efficient (?).

Finally, I have figured out how to read in exactly the contents of the file, even if it does not end with a newline :' )

@param s_fileName The full path and name of the file to be read. Must point to an existing and readable file. @exception AssertException Thrown when s_fileName is invalid or any IO error occurs */ public void appendFileText(StringOrBuffer str_orBfr, String s_fileName) { FileInputStream fis = null; Exception x = null;; try { fis = new FileInputStream(s_fileName); String s = null; byte[] ab = new byte[1]; while(fis.read(ab) != -1) { s = new String(ab); str_orBfr.append(s); ab = new byte[1]; } return; } catch(FileNotFoundException fnfx) { x = fnfx; } catch(IOException iox) { x = iox; } finally { IOException iox = null;; try { if(fis != null) { fis.close(); } } catch(IOException iox2) { iox = iox2; } finally { fis = null; //Throw the original exception. if(x != null && iox != null) { throwAX("appendFileText, while attempting to open the FileInputStream: " + x.toString() + ". Exception while attempting to close the FileInputStream: " + iox.toString() + "."); } else if(x != null) { throwAX("appendFileText, while attempting to open the FileInputStream: " + x.toString()); } else if(iox != null) { throwAX("appendFileText, while attempting to close the FileInputStream: " + iox.toString()); } } } } /**

Analyzes the provided code, and crashes with a specific error message if any multi-line comments are invalidly formatted.

Note: s_mlcStart may not be contained in any string, or this function will not work properly. It is suggested that, if you need to put s_mlcStart into any of your strings, then give the actual multiple-line comment start tag an extra asterisk ("/**" instead of "/*").

**/ public final void crashIfBadMLCs(StringOrBuffer sob_code, String s_mlcStart, String s_mlcEnd) { crashIfBadMLCStartEnd("stringMLComments", s_mlcStart, s_mlcEnd); int iIdx = 0; while(iIdx < sob_code.length()) { MLCStartEnd mlcse = new MLCStartEnd(sob_code, s_mlcStart, s_mlcEnd, iIdx); if(mlcse.iStart == -1 && mlcse.iEnd == -1) { //There are no more ml comments. Previous ml- //comments were already validated. return; } if(mlcse.iEnd == -1) { //There is a start tag, but no end tag. throwAX("crashIfBadMLCs: The next start tag (s_mlcStart='" + s_mlcStart + "') exists at index " + mlcse.iStart + ", but no corresponding end tag (s_mlcEnd='" + s_mlcEnd + "') exists after it."); } if(mlcse.iStart == -1) { //There is an end tag, but no start tag. throwAX("crashIfBadMLCs: The next end tag (s_mlcEnd='" + s_mlcEnd + "') exists at index " + mlcse.iEnd + ", but no corresponding start tag (s_mlcStart='" + s_mlcStart + "') exists before it."); } //Both a start and end tag were found and the start //tag exists before the end tag. //Does ANOTHER start tag exist before the closing tag? // //At the top of this function, we determined that the //start and end tags are not found in one another. //However (say the start and end tags are "[slash]**" and //"**[slash]"), another start tag may intersect the end tag: // // [slash]** [slash]**slash // //Say the start and end tags are "12345" and //"456". Here's how the start tag can intersect the //end: // // 12345 123456 // //With "456" and "12345", it is not possible for the //start tag to intersect the end tag. // //With "12345" and "5678": // // 12345 12345678 // //With "1", "12345", the start tag exists in the end, //which is moot by this point. // //So the only thing were concerned about is when the //start tag (1) finishes with the same character(s) as //the end tag begins and (2) another copy of the start //tag exists [less than starttag.length()] before the //end tag. // //It is not possible for them to overlap, because this //would mean that one exists entirely in the other. // //Therefore! (sheesh) // //(mlcse.iStart + s_mlcStart.length()) // //(mlcse.iEnd + s_mlcEnd.length() - 1) // //We end the search area with this, because it can only //overlap the end tag, at a maximum, up to but not //including the very last character. // //It may not overlap at all. Whether it does or not, //if any start tag is found in this search, then it is //an extra one, which is bad. Actually, in the case of //overlap, the end tag is considered to not exist at //all, given that analysis happens from left to right. int i1stCharAfterEnd = mlcse.iEnd + s_mlcEnd.length(); int iMLCStart_mid = sob_code.indexOf(s_mlcStart, (mlcse.iStart + s_mlcStart.length()), (i1stCharAfterEnd - 1)); if(iMLCStart_mid != -1) { throwAX("crashIfBadMLCs: Start comment tag (s_mlcStart='" + s_mlcStart + "') found at index " + mlcse.iStart + ", and end tag (s_mlcEnd='" + s_mlcEnd + "') was found at index " + mlcse.iEnd + ". However, another start tag was found before (or partially inside) the end tag, at index " + iMLCStart_mid + "."); } //This ml comment is valid. Advance to the first //character after the end tag. Since we analyze left //to right, it is not possible for the next start tag //to intersect this ending tag. iIdx = i1stCharAfterEnd; } } /**

String all multi-line comments out of the provided code. This function assumes that you have successfully called crashIfBadMLCs (ie, it did nothing/did not crash), otherwise this function will give you unpredictable results.

**/ public final void stripMLCs(StringOrBuffer sob_code, String s_mlcStart, String s_mlcEnd) { crashIfBadMLCStartEnd("stringMLComments", s_mlcStart, s_mlcEnd); try { int iIdx = 0; while(iIdx < sob_code.length()) { MLCStartEnd mlcse = new MLCStartEnd(sob_code, s_mlcStart, s_mlcEnd, iIdx); if(mlcse.iStart == -1 && mlcse.iEnd == -1) { //There are no more ml comments. Previous ml- //comments were already validated. return; } if(mlcse.iStart == -1) { //There are no more ml comments. Previous ml- //comments were already validated. return; } //An MLComment was found. sob_code.delete(mlcse.iStart, (mlcse.iEnd + s_mlcEnd.length())); iIdx = mlcse.iStart; } } catch(AssertException ax) { throwAX("stripMLCs: Please ensure that sob_code has valid multiple-line comments by passing it through crashIfBadMLCs. Original error: " + ax.toString()); } } /**

Eliminate all single line comments from the source code.

This eliminates all text from (and including) the single line comment delimiter, through the last character in the line. This leaves any preceding text (including tabs and spaces) alone. Also, this cannot distinguish an actual single line comment delimiter from one existing inside a string. Therefore, ensure that none exist unless they are truly delimiting a single line comment.

@param sob_sourceCode The StringOrBuffer containing source code, in which single line comments should be deleted. May not be null. @param s_slcDelimiter The single line comment delimiter. May not be null or zero characters in length. **/ public final void stripSLCs(StringOrBuffer sob_sourceCode, String s_slcDelimiter) { int iSLC = 0; while(true) { iSLC = sob_sourceCode.indexOf(s_slcDelimiter, iSLC); if(iSLC != -1) { sob_sourceCode.delete(iSLC, sob_sourceCode.indexOf(sLINE_SEP, iSLC)); } else { break; } } } /**

Eliminate the requested function from your JavaScript code. Assumes that the final "}" is the first "}" that has the same indentation as the "function " + s_function line. There may only be one space between "function" and s_function. Indentation may only contain tabs, no spaces.

**/ public final void elimJSFunction(String s_function, StringOrBuffer sob_code) { throwAXIfBadStr(s_function, "s_function", "elimJSFunction"); int iStart = sob_code.indexOf("function " + s_function + "("); char c = sob_code.charAt(iStart - 1); int iTabs = 0; while(c == '\t') { iTabs++; c = sob_code.charAt(iStart--); } String sTabs = uStr.getDuped("\t", iTabs); int iEnd = sob_code.indexOf(sLINE_SEP + sTabs + "}", iStart); if(iEnd == -1) { throwAX("elimJSFunction: No ending '}' found having the same indentation (" + iTabs + " tabs) as 'function " + s_function + "('"); } sob_code.delete(iStart, iEnd + (sLINE_SEP + sTabs + "}").length()); } /**

For all packages in the provided directory path, find out which packages are dependent on each other.

Note that the names of packages for the analyzed java files are based upon the last directory element in s_baseDir, not the actual "package" statements existing in the java code. For example, take this class: xbn.util.Utility

The first line of code in this file, Utility.java, is package xbn.util., meaning, of course, that its actual package is just that: xbn.util. However, if the full path and name of Utility.java were actually

C:\\temp\\xbn\\whatever\\Utility.java

and s_baseDir were provided as

C:\\temp\\xbn\\

Then this function would consider the fully qualified class name of Utility.java to be

xbn.whatever.Utility

In other words, the "package" line is ignored. Rather, the package for all classes is defined in this manner.


The first line of code creates a new DirFile with

new DirFile(s_baseDir, i_subDirLevels, (new FFJava()), optr_dbg)

Where FFJava is:

class FFJava implements FileFilter  {
public boolean accept(File f_ile)  {
   if(f_ile.isDirectory()  &&
         !f_ile.getPath().toLowerCase().startsWith("cvs")  &&
         !f_ile.getPath().toLowerCase().endsWith("cvs"))  {
      return true;
   }

   if(f_ile.getPath().toLowerCase().endsWith(".java"))  {
      return true;
   }

   return false;
}
}

@param s_baseDir The directory to analyze. Must end with a File.separator, and contain at least one File.separator before the last one. @param as_acceptPkgPre The names of package prefixes to recognize. If null, then all imported packages are reported. If non-null, then this contains all the packages to be reported on (all others are ignored and not reported). If non-null, must be at least one element in length, where all elements are non-null, unique, ordered ascending, and at least one character in length. @param b_crashIfSelfDependent If true, and a package depends on itself, then throw a descriptive error. As far as this function is concerned, if a package is dependent on itself, it means that a class within that package is explicitely importing another class from the same package. That import is therefore unneccessary. @param file_filter What packages/classes should be included/excluded. If null, then every *.java file within the search directories is analyzed. @return An array of s_acs_acs objects, each representing the name of a package (s_acs_acs.s)), the packages it depends on (s_acs_acs.acs1) and those depending on it (s_acs_acs.acs2). Each package name is fully qualified, as in 'xbn.util'. **/ public s_acs_acs[] getPackageDependencies(String s_baseDir, int i_subDirLevels, Outputter optr_dbg, String[] as_acceptPkgPre, boolean b_crashIfSelfDependent, FileFilter file_filter) { if(file_filter == null) { file_filter = (new FFJava()); } DirFile df = new DirFile(s_baseDir, i_subDirLevels, file_filter, optr_dbg); //Validate as_acceptPkgPre, and crash if bad. PASString pass = new PASString(as_acceptPkgPre, new PARString( new PARDupNullLen(false, true, (new RCLength(1, -1, false))), new PAROrderDir(true))); pass.isValid("xbn.util.Utility.getPackageDependencies", "as_acceptPkgPre"); VWString acsAcceptPre = new VWString(true); if(as_acceptPkgPre != null) { acsAcceptPre.addArray(as_acceptPkgPre); } //Get all the fully-qualified package names, in ascending //order. if(!s_baseDir.endsWith(File.separator)) { throwAX("getPackageDependencies: s_baseDir ('" + s_baseDir + "') does not end with File.separator ('" + File.separator + "')."); } //Get the index of the *second* to last file separator. int iIdxSlashB4Pkg = (new SOBString(s_baseDir)).lastIndexOf(File.separator, (s_baseDir.length() - File.separator.length()), true); if(iIdxSlashB4Pkg == -1) { throwAX("getPackageDependencies: No instance of File.separator ('" + File.separator + "') found in s_baseDir ('" + s_baseDir + "'), at least one must exist."); } optr_dbg.write("Getting all fully-qualified class names..."); ACSDepends acsdAllPkgs = getAllPackages(df, (new ACSDepends()), iIdxSlashB4Pkg, optr_dbg); optr_dbg.write("...SUCCESS"); //To prevent wasted processing if(optr_dbg.isOn()) { optr_dbg.write("------------------\nGetting dependencies...START\n------------------"); optr_dbg.write(df.toString()); } acsdAllPkgs = analyzeDependencies(acsdAllPkgs, optr_dbg, df, getPackageName(df, iIdxSlashB4Pkg), iIdxSlashB4Pkg, acsAcceptPre, b_crashIfSelfDependent, (new SOBStringBuffer(sES)), (new UtilSOB())); optr_dbg.write("------------------\nGetting dependencies...END\n------------------"); s_acs_acs[] aSacsacs = new s_acs_acs[acsdAllPkgs.size()]; for(int i = 0; i < acsdAllPkgs.size(); i++) { String sFQPackage = acsdAllPkgs.getString(i); aSacsacs[i] = new s_acs_acs(sFQPackage, acsdAllPkgs.getDependsOnACS(sFQPackage), acsdAllPkgs.getDependedOnByACS(sFQPackage)); } return aSacsacs; } private void crashIfBadMLCStartEnd(String s_callingFunc, String s_mlcStart, String s_mlcEnd) { i_i_i iii = uStr.getContainedIdxs(s_mlcStart, s_mlcEnd); if(iii != null) { if(iii.i1 == 0) { throwAX(s_callingFunc + ": s_mlcStart ('" + s_mlcStart + "') contains s_mlcEnd ('" + s_mlcEnd + "') at array index " + iii.i3 + "."); } else { throwAX(s_callingFunc + ": s_mlcStart ('" + s_mlcStart + "') contains s_mlcEnd ('" + s_mlcEnd + "') at array index " + iii.i3 + "."); } } } private ACSDepends getAllPackages(DirFile dir_file, ACSDepends acsd_pkgsSoFar, int i_idx2nd2LastSlash, Outputter optr_dbg) { if(acsd_pkgsSoFar.size() == 0) { String sPkg = getPackageName(dir_file, i_idx2nd2LastSlash); acsd_pkgsSoFar.addPackage(sPkg); optr_dbg.write("\t" + sPkg); } for(int i = 0; i < dir_file.getCountSubDirs(); i++) { DirFile dfSub = dir_file.getSubDir(i); String sPkg = getPackageName(dfSub, i_idx2nd2LastSlash); acsd_pkgsSoFar.addPackage(sPkg); optr_dbg.write("\t" + sPkg); acsd_pkgsSoFar = getAllPackages(dfSub, acsd_pkgsSoFar, i_idx2nd2LastSlash, optr_dbg); } return acsd_pkgsSoFar; } private String getPackageName(DirFile df_directory, int i_idxSlashB4Pkg) { //We are looking at a directory. What is the package //prefix for it? Package with slashes. String sPackage = df_directory.getFileObjectThis().getPath().substring(i_idxSlashB4Pkg + 1); //Package! return uStr.getReplaceAll(sPackage, File.separator, "."); } private ACSDepends analyzeDependencies(ACSDepends acs_depends, Outputter optr_dbg, DirFile dir_file, String s_package, int i_idxSlashB4Pkg, VWString acs_acceptPre, boolean b_crashIfSelfDependent, SOBStringBuffer sob_sb, UtilSOB util_sob) { sob_sb.deleteAll(); String sDebugPre = " " + (new UtilString()).getDuped(" ", dir_file.getLevelsBelowBaseDir()); for(int i = 0; i < dir_file.getCountSubFiles(); i++) { File f = dir_file.getSubFile(i).getFileObjectThis(); //"- 5" for ".java" String sJavaFile = f.getName().substring(0, f.getName().length() - 5); optr_dbg.write(sDebugPre + sJavaFile); appendFileText(sob_sb, f.getPath()); //Although the compiler knows that [slash][slash] //contained *in* a string is not the beginning of a //comment, I don't want to have to write code that //complicated, so make sure that [slash][slash] is //really only comments. final String sSLASH = "/"; stripSLCs(sob_sb, sSLASH + sSLASH); //Same with the multi-line comment delimiter. stripMLCs(sob_sb, sSLASH + "*", "*" + sSLASH); util_sob.replaceUntil(sob_sb, sLINE_SEP + sLINE_SEP, sLINE_SEP); FLRString flrs = new FLRString(sob_sb.getStringBuffer()); while(flrs.hasMoreLines()) { SOBStringBuffer ssbLine = new SOBStringBuffer(flrs.getNextLine()); ssbLine.trim(); if(ssbLine.length() < 1 || ssbLine.startsWith("package")) { continue; } if(ssbLine.startsWith("import")) { //7 is the character *2* after the "t", because //there's expected to be a space or tab there. //We want to eliminate the final dot, class name //and ending semicolon; ssbLine.trim(); ssbLine.delete(0, "import".length()); ssbLine.trim(); ssbLine.deleteCharAt(ssbLine.length() - ";".length()); String sFQImportClass = ssbLine.toString(); String sFQImportPkg = ssbLine.substring(0, sFQImportClass.lastIndexOf(".")); if(b_crashIfSelfDependent && s_package.equals(sFQImportPkg)) { throwAX("getPackageDependencies: Package '" + s_package + "' is dependent on itself, and b_crashIfSelfDependent equals true. The class '" + sJavaFile + "' is importing '" + sFQImportClass + "'."); } //If acs_acceptPre is non-null boolean bAccept = false; if(acs_acceptPre.size() == 0) { bAccept = true; } else { String sPkgPre = sFQImportPkg.substring(0, ssbLine.indexOf(".")); sPkgPre = sPkgPre.trim(); bAccept = acs_acceptPre.doesExist(sPkgPre); } if(bAccept) { if(!acs_depends.getDependsOnACS(s_package).doesExist(sFQImportPkg)) { optr_dbg.write(sDebugPre + " DEPENDS ON " + sFQImportPkg); acs_depends.addDependsOnPkg(s_package, sFQImportPkg); if(acs_depends.doesExist(sFQImportPkg)) { //If they want to keep track of packages //external to the directory they're //analyzing, the depended on by package //will not exist in acs_depends. acs_depends.addDependedOnByPkg(sFQImportPkg, s_package); } //Next four commented-out lines for debugging only // } else { // optr_dbg.write(sDebugPre + " NOT UNIQUE: " + sFQImportPkg); } // } else { // optr_dbg.write(sDebugPre + " UNACCEPTBL '" + sFQImportPkg + "'"); } } else { //All import statements have been retrieved. continue; } } } for(int i = 0; i < dir_file.getCountSubDirs(); i++) { DirFile dfSub = dir_file.getSubDir(i); String sPkg = getPackageName(dfSub, i_idxSlashB4Pkg); optr_dbg.write(sDebugPre + "[" + sPkg + "]"); acs_depends = analyzeDependencies(acs_depends, optr_dbg, dfSub, sPkg, i_idxSlashB4Pkg, acs_acceptPre, b_crashIfSelfDependent, sob_sb, util_sob); } return acs_depends; } } class FFJava 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")) { return true; } return false; } } class MLCStartEnd extends XBNObject { public int iStart = -1; public int iEnd = -1; public MLCStartEnd(StringOrBuffer sob_code, String s_mlcStart, String s_mlcEnd, int i_idx) { iStart = sob_code.indexOf(s_mlcStart, i_idx); iEnd = sob_code.indexOf(s_mlcEnd, i_idx); if(iStart == -1 && iEnd == -1) { //There are no more ml comments. Previous ml- //comments were already validated. return; } //A start and/or end tag was found. IOW, at least one //mlc tag was found, either start, or end, or //start+end... // //...although not neccesarily in that order. if(iStart != -1 && iEnd != -1) { //Both a start tag and end tag were found. if(iEnd < iStart) { //The end tag is either completely before the //start tag, or intersects the beginning of the //it. throwAX("crashIfBadMLCs: The next end tag (s_mlcEnd='" + s_mlcEnd + "') exists at index " + iEnd + ", but no corresponding start tag (s_mlcStart='" + s_mlcStart + "') exists before it."); } //The end tag does not begin before the start tag //begins. //As determined above, the end tag is not contained //in the start tag (and vice-versa). Therefore, the //end tag does not begin at the same index as the //start tag. int iCharAfterStart = (iStart + s_mlcStart.length()); if(iEnd < iCharAfterStart) { //The (first characters of) the end tag and the //(last of the) start tag intersect. Since we //analyze left to right, THIS end tag is //considered to not exist at all. // //Analyzing from left to right causes the second //thing overlapping/intersecting to be ignored, //and "eliminated" before further analysis. For //example, say the start tag is "123", and the //end is "345". If the ml comment block is... // // 12345 comment comment comment 345 // //...this is perfectly legal. Although an end //tag does exist (beginning) in the start tag, //since we analyze left to right, the start tag //is "eliminated" before proceeding. So after //the start tag is found, we're left with // // 45 comment comment comment 345 // //What remains at the start is not an end tag, //and this ml comment is therefore legal. //We need to search for the NEXT end tag that //actually "exists". iEnd = sob_code.indexOf(s_mlcEnd, iCharAfterStart); } } //We *still* know that at least one of the start and //end tags exist. However, if *both* existed before //the previous if block, it is now possible that the //end tag "no longer" exists. If both still exist, //then we know they're in the correct order (start then //end). } } class ACSDepends extends VWString { public final VWObject acoAcsDependsOn = new VWObject(); public final VWObject acoAcsDependedOnBy = new VWObject(); public ACSDepends() { super(true); } public final void addPackage(String s_package) { add(s_package); acoAcsDependsOn.add(new VWString(true)); acoAcsDependedOnBy.add(new VWString(true)); } public final void addDependsOnPkg(String s_package, String s_dependsOnPkg) { getDependsOnACS(s_package).add(s_dependsOnPkg); } public final void addDependedOnByPkg(String s_package, String s_dependedOnByPkg) { getDependedOnByACS(s_package).add(s_dependedOnByPkg); } public final VWString getDependsOnACS(String s_package) { return ((VWString)acoAcsDependsOn.getObject(getFoundIdx(s_package))); } public final VWString getDependedOnByACS(String s_package) { return ((VWString)acoAcsDependedOnBy.getObject(getFoundIdx(s_package))); } }