Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
I'm practicing my Java programming, and found, what I guess is an alias, while drilling through all my directories. (Yes, it has an "@" sign at the end of the name when issuing a "ls -aF")

This directory:

/automount/Servers/todd-burchs-computer.local

points back to the root directory ( / ). (Substitute "todd-burchs-computer" for your Computer's name).

If there a way to tell, in Java, that is is an alias? Would File.isAbsolute() fit the bill?

Thanks, Todd
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
Here's the code if anyone wants to see it. On my machine, running against "~/Documents" yields 324 files (15 hidden) in 46 directories. Running against "/Library" yields 175993 files (173 hidden) in 27899 directories. Running against "/" yields an infinite loop.

Also, any constructive critiques on the code are most welcome.

Thanks, Todd.

To run, compile and then type

java Traverse ~/Documents
or
java Traverse /Library
or
java Traverse if you want to see the loop. ;)
(Apple+period to break out of the loop)


Code:
/*  Java program to traverse the entire directory structure.  */ 
public class Traverse { 
	static final char indent_char = '.' ; 
	static int indent = 0 ; 
	static String pad = "" ; 
	static int num_files, num_directories, num_hidden ; 
	
	public static void main(String[] args) { 
		String dir ; 				
		System.out.println("There are " + args.length + " args.") ; 
		if (args.length==0) dir = "/" ; 
		else dir = args[0] ;  
		System.out.println("Passed argument is ->" + dir + "<-") ; 
		DriveType(dir) ; 
		System.out.println("There were " + num_files + " files (" + num_hidden + 
						   " hidden) in " + num_directories + " directories.") ; 
	}
	
	private static void DriveType(String thing) { 
		indent(1) ; 
		java.io.File myFile = new java.io.File(thing) ; 
		if (myFile.isDirectory() ) {
			num_directories++ ;  
			System.out.println(indent + ": " + pad + "Directory-> " + thing + " (#" + num_directories + ")") ; 
			ListFiles(myFile) ; 
		}
		indent(-1) ; 
	} 
	
	private static void indent(int amount) { 
		indent = indent + amount ; 
		if (amount > 0 ) pad = pad + indent_char ;  
		else pad = pad.substring(1) ; 
	} 
	
	private static void ListFiles(java.io.File file) { 
		java.util.Vector<String> directories = new java.util.Vector<String>() ;  
		int i ; 
		
		String[] members = file.list() ;  // get list of all files and directories 	
		if (members==null) return ;       // Might be null if permission denied 
		// Separate the files from the directories. 
		for (String s : members) {  
			java.io.File tempFile = new java.io.File(file.getPath() + java.io.File.separatorChar + s) ; 		
			if ( tempFile.isDirectory() ) directories.add(s) ; 
			else {
				num_files++ ; 
				System.out.println(indent+": " + pad + "File-> " + s + " (#" + num_files + ")") ; 
				if ( tempFile.isHidden() ) num_hidden++ ; 
			} 
		}
		// Now, parse all the directories. 
		for (String s : directories) DriveType(file.getPath() + java.io.File.separatorChar + s ) ; 
	} 
}
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
After fixing the program, here's the complete working example. On my machine, output is "There were 514867 files (399 hidden) in 115121 directories." Takes a couple minutes to run.

What I had to do was compare the canonical name with the path name. When they were not equal, then the path name was symbolic, and I should not have drilled into it.

Here's the code, for anyone who might care. The bold portions, basically, are the new/changed code.

Code:
/*  Java program to traverse the entire directory structure.  */ 
public class Traverse { 
	static final char indent_char = '.' ; 
	static int indent = 0 ; 
	static String pad = "" ; 
	static int num_files, num_directories, num_hidden ; 
	
	public static void main(String[] args) { 
		String dir ; 				
		System.out.println("There are " + args.length + " args.") ; 
		if (args.length==0) dir = "/" ; 
		else dir = args[0] ;  
		System.out.println("Passed argument is ->" + dir + "<-") ; 
		DriveType(dir) ; 
		System.out.println("There were " + num_files + " files (" + num_hidden + 
						   " hidden) in " + num_directories + " directories.") ; 
	}
	
	private static void DriveType(String thing) { 
		[b]String tempPath = " "  ;[/b] 
		indent(1) ; 
		java.io.File myFile = new java.io.File(thing) ; 
		if (myFile.isDirectory() ) {
			[b]try { 
				tempPath = myFile.getCanonicalFile().getPath() ;  // reduce path/name to simplest form 
			}
			catch (Exception e) { 
				System.out.println("*** Got Exception ***") ; 
				System.exit(8) ; 
			} 
			if ( tempPath.equalsIgnoreCase( myFile.getPath() ) )[/b] {
				num_directories++ ;  			
				System.out.println(indent + ": " + pad + "Directory-> " + thing + " (#" + num_directories + ")") ; 
				ListFiles(myFile) ; 
			}
		}
		indent(-1) ; 
	} 
	
	private static void indent(int amount) { 
		indent = indent + amount ; 
		if (amount > 0 ) pad = pad + indent_char ;  
		else pad = pad.substring(1) ; 
	} 
	
	private static void ListFiles(java.io.File file) { 
		java.util.Vector<String> directories = new java.util.Vector<String>() ;  
		int i ; 
		
		String[] members = file.list() ;  // get list of all files and directories 	
		if (members==null) return ;       // Might be null if permission denied 
		// Separate the files from the directories. 
		for (String s : members) {  
			java.io.File tempFile = new java.io.File(file.getPath() + java.io.File.separatorChar + s) ; 		
			if ( tempFile.isDirectory() ) directories.add(s) ; 
			else {
				num_files++ ; 
				System.out.println(indent+": " + pad + "File-> " + s + " (#" + num_files + ")") ; 
				if ( tempFile.isHidden() ) num_hidden++ ; 
			} 
		}
		// Now, parse all the directories. 
		for (String s : directories) DriveType(file.getPath() + java.io.File.separator + s ) ; 
	} 
}
 

jeremy.king

macrumors 603
Jul 23, 2002
5,479
1
Holly Springs, NC
code review

Glad to see you got it working, here's some feedback on the actual code.

1. remember method names should begin with a lowercase letter, then use camelcase
2. you have a method with the same name as a member, some would argue this is poor form because of ambiguity
3. class name should be a noun
4. why is everything static? consider making these instance methods, with an instantiation of Traverse in the main method.
5. Always use braces with if statements, as recommended by the Java Code Conventions
[optional]
6. Javadoc your methods and class


See http://java.sun.com/docs/codeconv/ for more information.
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
Thanks Jeremy. I've updated everything you pointed out - thanks for taking the time to provide feedback.

For the braces after the if's, I was not aware of the Java Code Convention to use them all the time, and I had actually removed them to keep my prior posts a bit smaller / tighter. Now, they're back.

Javadoc is pretty cool. At first I thought it wasn't working because only the main() method was being formatted/documented, but then my friend, who codes professionally in Java evvery day, pointed out the -private switch.

Here's the updated code, posted for posterity. Thanks again. Todd

For other newbies like me, to run javadoc, enter:

javadoc -private FileSystemTraverse.java

Code:
/*  Java program to traverse the entire directory structure.  */ 
/** 
* This class traverses the file system, reporting on found files and directories. 
* It starts with either the root directory or with the passed directory name.
*/ 
public class FileSystemTraverse { 
	/**
	* This is a constant that holds the indentation character.  It could be a blank, 
	* a period (the default), a hyphen, or any other character you choose. 
	*/ 
	static final char indent_char = '.' ; 
	
	/**
	* This is a class variable that is used to keep track of indentation levels. 
	*/ 
	static int indent = 0 ; 
	/** 
	* This holds the string of characters that prefix each line of output. 
	*/ 
	static String pad = "" ; 
	/**
	* Holds the number of files found.  Does not include files that require 
	* additional permission.
	*/ 
	static int num_files ; 
	/** 
	* Holds the number of directories found.  Does not include directories that 
	* require additional permission. 
	*/
	static int num_directories ; 
	/** 
	* The number of hidden files found.
	*/
	static int num_hidden ; 
	
	/** 
	* The main() method can be passed an optional argument.  The argument, if passed, 
	* is expected to be a file name or directory name.  
	* <br />
	* If an argument is not passed, a default directory name of "/" is assigned. 
	* <br />
	* Then, routeObject() is passed to make a determination whether or not the 
	* argument is a file or directory. 
	* 
	* @param args The passed String - a file name or directory name 
	* @author Todd Burch
	* @version 1.00  March 19, 2007 
	*	
	*/ 
	
	public static void main(String[] args) { 
		/** 
		* Holds either the passed argument or a default value.  This is the initial 
		* string the traversal process starts with. 
		*/ 
		String dir ; 				
		System.out.println("There are " + args.length + " args.") ; 
		if (args.length==0) { 
			dir = "/" ; 
		} 
		else dir = args[0] ;  
		System.out.println("Passed argument is ->" + dir + "<-") ; 
		FileSystemTraverse t = new FileSystemTraverse() ; 
		t.routeObject(dir) ; 
		System.out.println("There were " + t.num_files + " files (" + t.num_hidden + 
						   " hidden) in " + t.num_directories + " directories.") ; 
	}

	
	/** 
	* The routeObject() method accepts a string as input.  The string is a full path name 
	* and is expected to represent a valid file name or a valid directory. A determination 
	* is made to whether the passed string represents a file or directory.  If a directory, 
	* listFiles() is called to get a list of all the files.  If it is a file, nothing is done. 
	* <br /> 	
	* A check is made to see if the passed string represents a symbolic (an alias).  If it 
	* does, it is ignored and control is returned to the caller. 
	*/ 
	private void routeObject(String thing) { 
		String tempPath = " "  ; 
		this.doIndent(1) ; 
		java.io.File myFile = new java.io.File(thing) ; 
		if (myFile.isDirectory() ) {
			try { 
				tempPath = myFile.getCanonicalPath() ;  // reduce path/name to simplest form 
			}
			catch (Exception e) { 
				System.out.println("*** Got Exception ***") ; 
				System.exit(8) ; 
			} 
			if ( tempPath.equalsIgnoreCase( myFile.getPath() ) ) {
				num_directories++ ;  			
				System.out.println(indent + ": " + pad + "Directory-> " + thing + " (#" + num_directories + ")") ; 
				this.listFiles(myFile) ; 
			}
		}
		this.doIndent(-1) ; 
	} 
	
	/** 
	* The doIndent() method control the indention of the report.  It is passed a 
	* 1 or -1.  
	* <br /> 
	* If a 1, the pad string is increased by one pad character.  Otherwise, the pad 
	* string is reduced by one character. 
	*/ 
	
	private void doIndent(int amount) { 
		indent = indent + amount ; 
		if (amount > 0 ) { 
			pad = pad + indent_char ;  
		}
		else pad = pad.substring(1) ; 
	} 
	
	/** 
	* The listFiles() method is passed a File object that represents a directory. 
	* A list of all the files and directories contained therein are saved.  
	* <br /> 
	* If a member is file, it's name is written to the report. 
	* If a member is a directory, the full directory name is saved to an array. 
	* Files with the hidden attribute are counted. 
	* <br /> 
	* For each directory in the array, call routeObject(). 
	*/ 
	
	private void listFiles(java.io.File file) { 
		java.util.Vector<String> directories = new java.util.Vector<String>() ;  
		int i ; 
		
		String[] members = file.list() ;  // get list of all files and directories 	
		if (members==null) {
			return ;       // Might be null if permission denied 
		}
		// Separate the files from the directories. 
		for (String s : members) {  
			java.io.File tempFile = new java.io.File(file.getPath() + java.io.File.separatorChar + s) ; 		
			if ( tempFile.isDirectory() ) {
				directories.add(s) ; 
			}
			else {
				num_files++ ; 
				System.out.println(indent+": " + pad + "File-> " + s + " (#" + num_files + ")") ; 
				if ( tempFile.isHidden() ) num_hidden++ ; 
			} 
		}
		// Now, parse all the directories. 
		for (String s : directories) this.routeObject(file.getPath() + java.io.File.separator + s ) ; 
	} 
}
 

jeremy.king

macrumors 603
Jul 23, 2002
5,479
1
Holly Springs, NC
Don't forget to also change your member variables to instance variables (ie. not static). Actually indent_char should be renamed to be all caps because it is a constant, and you can leave it as static final too...

Otherwise, a nice solid piece of work there. Well done toddburch! :)

PS. No worries about braces, even the Sun programmers who wrote Java don't always use them. Actually it's pretty interesting if you start to explore the JDK libraries source...
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.