Monday, October 28, 2013

Path class in java

Java 7 introduces a new set of I/O APIs called NIO.2 that offer convenient ways to perform operations related to a file system. In this post, you will explore Path class and how it is useful.





Initially, Java offered the File class (in the java.io package) to access file systems. 
This class represents a file/directory in the file system and allows you to perform operations such as checking the existence of a file/directory,getting the properties, and deleting a file/directory











Following shortcomings were noticed in the first version of the Java I/O APIs:


  • The File class lacked the significant functionality required to implement even commonly used functionality. For instance, it lacked a copy method to copy a file/directory.
  • The File class defined many methods that returned a Boolean value. Thus, in case of an error, false was returned, rather than throwing an exception, so the developer had no way of knowing why that call failed.
  • The File class did not provide good support for handling symbolic links.
  • The File class handled directories and paths in an inefficient way (it did not scale well).
  • The File class provided access to a very limited set of file attributes, which was insufficient in many situations.



What Is a Path?





A file system stores and organizes files on some form of media, generally one or more hard drives, in such a way that they can be easily retrieved. 

Most file systems in use today store the files in a tree (or hierarchical) structure. At the top of the tree is one (or more) root nodes.

 Under the root node, there are files and directories . Each directory can contain files and subdirectories, which in turn can contain files and subdirectories, and so on, potentially to an almost limitless depth.








Relative or Absolute?





A path is either relative or absolute. 


An absolute path always contains the root element and the complete directory list required to locate the file. 
For example, /home/sally/statusReport is an absolute path.
All of the information needed to locate the file is contained in the path string.



A relative path needs to be combined with another path in order to access a file.
For example, joe/foo is a relative path.
Without more information, a program cannot reliably locate the joe/foo directory in the file system.




Symbolic Links



A symbolic link is a special file that serves as a reference to another file. 

For the most part, symbolic links are transparent to applications, and operations on symbolic links are automatically redirected to the target of the link.

Exceptions are when a symbolic link is deleted, or renamed in which case the link itself is deleted, or renamed and not the target of the link.

In the left figure, logFile appears to be a regular file to the user, but it is actually a symbolic link to dir/logs/HomeLogFile. HomeLogFile is the target of the link.





Java 7 introduces a new programming abstraction for path, namely the Path interface. This Path abstraction is used in new features and APIs throughout NIO.2, so it is an important interface to understand. 
A path object contains the names of directories and files that make the full path of the file/directory represented by the Path object; the Path abstraction provides methods to extract path elements, manipulate them, and append them.

In Java 6 and earlier you do that:

File file = new File("README.TXT");

In Java 7 you do that:

Path path = Paths.get("README.TXT");

To make the migration to Java 7 easier, the File class has a new method toPath() that allows you to transform File to Path:

Path path = new File("README.TXT").toPath();


Lets's have a simple example of Path :
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
public class SimplePathDemo {
 public static void main(String[] args) {
  Path path = Paths.get("C:\\Android\\JavaLatte.txt");
  Path path1 = Paths.get(URI.create("C:\\Android\\JavaLatte.txt"));
  Path path2 = FileSystems.getDefault().getPath("C:\\Android\\JavaLatte.txt");
  //retrieve basic information;
  String str=path.toString();
  System.out.println("path : "+str);
  System.out.println("File Name : "+path.getFileName());
  System.out.println("Root of the path : "+path.getRoot());
  System.out.println("No. of file/directory name in path : "+path.getNameCount());
  System.out.println("Is abosolute : "+path.isAbsolute());
  System.out.println("Path elements : ");
  for(Path element : path){
   System.out.println(element);
  }
 }

}
Sample Output
path : C:\Android\JavaLatte.txt
File Name : JavaLatte.txt
Root of the path : C:\
No. of file/directory name in path : 2
Is abosolute : true
Path elements :
Android
JavaLatte.txt

Important Methods in the Path Interface

MethodDesciption
Path getRoot() Returns a Path object representing the root of the given path,
or null if the path does not have a root.
Path getFileName() Returns the file name or directory name of the given path. Note
that the file/directory name is the last element or name in the
given path.
Path getParent() Returns the Path object representing the parent of the given
path, or null if no parent component exists for the path
int getNameCount() Returns the number of file/directory names in the given path;
returns 0 if the given path represents the root.
Path subpath(int beginIndex, int endIndex) Returns a Path object that is part of this Path object; the
returned Path object has a name that begins at beginIndex
till the element at index endIndex - 1. In other words,
beginIndex is inclusive of the name in that index and
exclusive of the name in endIndex. This method may throw
IllegalArgumentException if beginIndex is >= number of
elements, or endIndex <= beginIndex, or endIndex is > number
of elements.
Path normalize() Removes redundant elements in path such as . (dot symbol
that indicates current directory) and .. (double dot symbol that
indicates parent directory).
Path resolve(Path other)
Path resolve(String other)
Resolves a path against the given path. For example, this
method could combine the given path with the other path and
return the resulting path.
Boolean isAbsolute() Returns true if the given path is an absolute path; returns false
if not (
Path startsWith(String path)
Path startsWith(Path path)
Returns true if this Path object starts with the given path, or
else returns false.
Path toAbsolutePath() Returns the absolute path


We'll see first some methods details and then example of each of them.

Removing Redundancies From a Path

Many file systems use "." notation to denote the current directory and ".." to denote the parent directory. You might have a situation where a Path contains redundant directory information.
Perhaps a server is configured to save its log files in the "/dir/logs/." directory, and you want to delete the trailing "/." notation from the path.

The normalize method removes any redundant elements, which includes any "." or "directory/.." occurrences. 

Converting a Path
The toUri() method returns the URI (a path that can be opened from a browser) from the path.
The toAbsolutePath() method returns the absolute path from a given relative path. In case the input path is already an absolute path, the method returns the same object. The toAbsolutePath method converts the user input and returns a Path that returns useful values when queried. The file does not need to exist for this method to work.
The toRealPath method returns the real path of an existing file. This method performs several operations in one:
  1. If true is passed to this method and the file system supports symbolic links, this method resolves any symbolic links in the path.
  2. If the Path is relative, it returns an absolute path.
  3. If the Path contains any redundant elements, it returns a path with those elements removed.
This method throws an exception if the file does not exist or cannot be accessed. You can catch the exception when you want to handle any of these cases


Joining Two Paths
You can combine paths by using the resolve method. You pass in a partial path , which is a path that does not include a root element, and that partial path is appended to the original path.
                Path joinPath = Paths.get("/home");
System.out.println("Join Path : "+joinPath.resolve("pkumar/test"));


Creating a Path Between Two Paths

A common requirement when you are writing file I/O code is the capability to construct a path from one location in the file system to another location. You can meet this using the relativize method. This method constructs a path originating from the original path and ending at the location specified by the passed-in path. The new path is relative to the original path.
Path p1 = Paths.get("/home");
Path p2 = Paths.get("/home/pkumar/test/BlogCode");


To navigate from home to BlogCode, you first navigate one level down to pkumar, one leve down to test and then one more level down to BlogCode.
Navigating from BlogCode to home requires moving up 3 levels.

Comparing Two Paths

The Path interface provides two methods to compare two Path objects: equals() and compareTo().

The equals() method checks the equality of two Path objects and returns a Boolean value.
when compareTo() compares two Path objects character by character and returns an integer: 0 if both Path objects are equal; a negative integer if this path is lexicographically less than the parameter path; and a positive integer if this path is lexicographically greater than the parameter path.



import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class PathDemo2 {
 public static void main(String[] args) {
  Path path = Paths.get("./JavaLatte.txt");
  Path normalizePath = path.normalize();
  System.out.println("Normalized path : "+normalizePath.toString());
  System.out.println("Normalized path : "+normalizePath.toUri());

  Path path1 = Paths.get("test.txt");
  Path AbsolutePath = path1.toAbsolutePath();
  System.out.println("Absoulete path (your current working dir) : "+AbsolutePath.toString());

  Path path2 = Paths.get("/home/pkumar/test.txt");
  Path AbsolutePath2 = path2.toAbsolutePath();
  System.out.println("Absoulete path (already absoulete) : "+AbsolutePath2.toString());
  System.out.println();

  Path realPath = null;
  Path path3 = Paths.get("JavaLatte.txt");
  try {
   realPath = path3.toRealPath();
  } catch (NoSuchFileException e) {
   //Logic for case when file doesn't exist
  }catch (IOException e1){
   //Logic for other sort of file error
  }
  System.out.println("Real Path : "+realPath.toString());
  System.out.println();  
  Path subPath = realPath.subpath(3, 4);
  System.out.println("SubPath(3,4) : "+subPath.toString());
  Path subPath1 = realPath.subpath(2, 3);
  System.out.println("SubPath(3,4) : "+subPath1.toString());
  System.out.println("Root (/ - linux or C:\\ - windows) : "+realPath.getRoot().toString());

  Path joinPath = Paths.get("/home");
  System.out.println("Join Path : "+joinPath.resolve("pkumar/test"));

  Path connectPath1 = Paths.get("/home");
  Path connectPath2 = Paths.get("/home/pkumar/test/BlogCode");
  System.out.println();
  System.out.println("Creating a Path Between Two Paths");
  System.out.println();
  System.out.println("home to BlogCode : "+connectPath1.relativize(connectPath2).toString());
  System.out.println("BlogCode to home : "+connectPath2.relativize(connectPath1).toString());
  System.out.println();
  Path connectPath3 = Paths.get("/pkumar");
  Path connectPath4 = Paths.get("/home/pkumar/test/BlogCode");
  System.out.println("pkumar to BlogCode : "+connectPath3.relativize(connectPath4).toString());
  System.out.println("BlogCode to pkumar : "+connectPath4.relativize(connectPath3).toString());
  System.out.println();
  System.out.println("Comparing path");
  Path comparePath1 = Paths.get("/home/pkumar/test/BlogCode");
  Path comparePath2 = Paths.get("/BlogCode");
  System.out.println("/home/pkumar/test/BlogCode compareto /BlogCode : "+comparePath1.compareTo(comparePath2));
  System.out.println("/BlogCode compareto /home/pkumar/test/BlogCode : "+comparePath2.compareTo(comparePath1));
  System.out.println("/BlogCode compareto /BlogCode : "+comparePath2.compareTo(comparePath2));

 }


Sample Output

Normalized path : JavaLatte.txt
Normalized path : file:///home/pkumar/test/BlogCode/JavaLatte.txt
Absoulete path (your current working dir) : /home/pkumar/test/BlogCode/test.txt
Absoulete path (already absoulete) : /home/pkumar/test.txt

Real Path : /home/pkumar/test/BlogCode/JavaLatte.txt

SubPath(3,4) : BlogCode
SubPath(3,4) : test
Root (/ - linux or C:\ - windows) : /
Join Path : /home/pkumar/test

Creating a Path Between Two Paths

home to BlogCode : pkumar/test/BlogCode
BlogCode to home : ../../..

pkumar to BlogCode : ../home/pkumar/test/BlogCode
BlogCode to pkumar : ../../../../pkumar

Comparing path
/home/pkumar/test/BlogCode compareto /BlogCode : 38
/BlogCode compareto /home/pkumar/test/BlogCode : -38
/BlogCode compareto /BlogCode : 0




Note : Implementations of java.nio.file.Path interface are immutable and safe for use by multiple concurrent threads.



If you know anyone who has started learning java, why not help them out! Just share this post with them. 
Thanks for studying today!...

1 comment: