package javaxt.io;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.io.*;
import java.util.Locale;
//******************************************************************************
//** File Class
//******************************************************************************
/**
* Used to represent a single file on a file system. In many ways, this class
* is an extension of the java.io.File class. However, unlike the java.io.File
* class, this object provides functions that are relevant and specific to
* files (not directories).
*
******************************************************************************/
public class File implements Comparable {
private java.io.File file; //<--DO NOT USE DIRECTLY! Use getFile() instead.
private String name = "";
private String path = "";
private FileAttributes attr;
private long lastAttrUpdate = 0;
public final String PathSeparator = System.getProperty("file.separator");
public final String LineSeperator = System.getProperty("line.separator");
private static final boolean isWindows = Directory.isWindows;
private int bufferSize = 1024*1024; //1MB
//public static final boolean jni = loadDLL();
//**************************************************************************
//** Constructor
//**************************************************************************
/** Creates a new File instance by converting the given pathname string into
* an abstract pathname.
*/
public File(String Path) {
if (Path == null) {
throw new NullPointerException();
}
if (Path.startsWith("\"") && Path.endsWith("\"")){
Path = Path.substring(1,Path.length()-1).trim();
}
if (Path.endsWith("\\") || Path.endsWith("/")){
throw new IllegalArgumentException("Invalid Path: " + Path);
}
init(Path);
//int idx = Path.replace("\\", "/").lastIndexOf("/")+1;
//path = Path.substring(0, idx);
//name = Path.substring(idx);
}
//**************************************************************************
//** Constructor
//**************************************************************************
/** Instantiates this class using a java.io.File. Please use the Directory
* class for directories. Example:
* if (file.isDirectory()) new Directory(file);
*/
public File(java.io.File File) {
if (File==null) {
throw new NullPointerException();
}
if (File.isDirectory()){
throw new IllegalArgumentException("Invalid Path.");
}
String p = File.getAbsolutePath();
init(p);
if (p.equals(toString())){
this.file = File;
}
}
//**************************************************************************
//** Constructor
//**************************************************************************
/** Creates a new File instance from a parent abstract pathname and a child
* pathname string.
*/
public File(java.io.File Parent, String Child){
this(Parent.getPath(), Child);
}
public File(Directory Parent, String Child){
this(Parent.getPath(), Child);
}
public File(String Parent, String Child){
if (Parent == null || Child == null) {
throw new NullPointerException();
}
//Normalize the strings
Parent = Parent.replace("\\", "/");
Child = Child.replace("\\", "/");
//Combine the Parent and Child into a single path. This is done in
//case the Child variable contains additional path information.
if (!Parent.endsWith("/")) Parent += "/";
if (Child.startsWith("/")) Child = Child.substring(1);
if (Child.endsWith("/")) Child = Child.substring(0, Child.length()-1);
init(Parent + Child);
}
/** Used to parse the absolute path to the file. For performance reasons,
* we do not rely on the java.io.File to determine the path and name
* variables.
*/
private void init(String Path){
Path = Path.replace("\\", "/");
String[] arr = Path.split("/");
if (arr.length>1){
name = arr[arr.length-1];
path = Path.substring(0, Path.lastIndexOf(name));
}
else{
name = arr[0];
path = "./";
}
if (path.isEmpty()) path = PathSeparator;
else path = path.replace("/", PathSeparator);
if (!path.endsWith(PathSeparator)) path += PathSeparator;
//Special case for relative paths
if (path.startsWith(".")){
try{
init(new java.io.File(path + name).getCanonicalPath());
}
catch(Exception e){
throw new IllegalArgumentException("Invalid Path.");
}
}
}
//**************************************************************************
//** Get File Name
//**************************************************************************
/** Returns the name of the file, excluding the path. */
public String getName(){
return name;
}
//**************************************************************************
//** Get File Name
//**************************************************************************
/** Returns the name of the file, excluding the path.
*
* @param IncludeFileExtension If true, includes the file extension.
* Otherwise, will return the file name without the extension.
*/
public String getName(boolean IncludeFileExtension){
if (!IncludeFileExtension){
int idx = name.lastIndexOf(".");
if (idx > -1) return name.substring(0, idx);
}
return name;
}
//**************************************************************************
//** Get File Path
//**************************************************************************
/** Used to retrieve the path to the file, excluding the file name. Appends
* a file separator to the end of the string.
*/
public String getPath(){
return path;
}
//**************************************************************************
//** Get Directory
//**************************************************************************
/** Returns the file's parent directory. Same as getParentDirectory() */
public Directory getDirectory(){
return getParentDirectory();
}
//**************************************************************************
//** Get Parent Directory
//**************************************************************************
/** Returns the file's parent directory. Returns null if the parent
* directory is unknown.
*/
public Directory getParentDirectory(){
if (path.length()>0) return new Directory(path);
else return null;
}
//**************************************************************************
//** toFile
//**************************************************************************
/** Returns the java.io.File representation of this object. */
public java.io.File toFile(){
return getFile();
}
//**************************************************************************
//** getFile
//**************************************************************************
/** Returns the java.io.File representation of this object. */
private java.io.File getFile(){
if (file==null) file = new java.io.File(path + name);
return file;
}
//**************************************************************************
//** Get File Extension
//**************************************************************************
/** Returns the file's extension, excluding the last dot/period
* (e.g. "C:\image.jpg" will return "jpg"). Returns a zero-length string
* if there is no extension.
*/
public String getExtension(){
int idx = name.lastIndexOf(".");
if (idx > -1) return name.substring(idx+1);
else return "";
}
//**************************************************************************
//** getSize
//**************************************************************************
/** Returns the size of the file, in bytes. Returns 0L if the file is does
* not exist or if the object is a directory.
*/
public long getSize(){
if (file!=null) return getFileSize();
try{
FileAttributes attr = getFileAttributes();
if (attr!=null) return attr.getSize();
else{
getFile();
return getFileSize();
}
}
catch(java.io.FileNotFoundException e){
return 0L;
}
}
private long getFileSize(){
long size = file.length();
if (size>0L && file.isFile()) return size;
else return 0L;
}
//**************************************************************************
//** getDate
//**************************************************************************
/** Returns a timestamp of when the file was last modified. Returns null if
* the file does not exist or if the object is a directory.
*/
public java.util.Date getDate(){
if (file!=null) return getFileDate();
try{
FileAttributes attr = getFileAttributes();
if (attr!=null) return attr.getLastWriteTime();
else{
getFile();
return getFileDate();
}
}
catch(java.io.FileNotFoundException e){
return null;
}
}
private java.util.Date getFileDate(){
if (file.exists() && file.isFile()) return new java.util.Date(file.lastModified());
else return null;
}
//**************************************************************************
//** setDate
//**************************************************************************
/** Used to set/update the last modified date.
*/
public boolean setDate(java.util.Date lastModified){
if (lastModified!=null){
java.io.File file = getFile();
if (file.exists() && file.isFile()){
long t = lastModified.getTime();
if (getDate().getTime()!=t){
attr = null;
return getFile().setLastModified(t);
}
}
}
return false;
}
//**************************************************************************
//** Exists
//**************************************************************************
/** Used to determine whether a file exists. Returns false if the file
* system can't find the file or if the object is a directory.
*/
public boolean exists(){
if (file!=null){
return (file.isFile() && file.exists());
}
try{
FileAttributes attr = getFileAttributes();
if (attr!=null) return true;
else{
getFile();
return (file.isFile() && file.exists());
}
}
catch(java.io.FileNotFoundException e){
return false;
}
}
//**************************************************************************
//** isHidden
//**************************************************************************
/** Used to check whether the file is hidden. Returns true if the file
* exists and is hidden according to the conventions of the underlying
* file system.
*/
public boolean isHidden(){
if (file!=null){
return (file.isFile() && file.isHidden());
}
try{
FileAttributes attr = getFileAttributes();
if (attr!=null) return attr.isHidden();
else{
getFile();
return (file.isFile() && file.isHidden());
}
}
catch(java.io.FileNotFoundException e){
return false;
}
}
//**************************************************************************
//** isReadOnly
//**************************************************************************
/** Used to check whether the file has write permissions. Returns true if
* the file exists and the application is not allowed to write to the file.
*/
public boolean isReadOnly(){
if (file!=null){
return (file.isFile() && !file.canWrite());
}
try{
FileAttributes attr = getFileAttributes();
if (attr!=null) return attr.isReadOnly();
else{
getFile();
return (file.isFile() && !file.canWrite());
}
}
catch(java.io.FileNotFoundException e){
return false;
}
}
//**************************************************************************
//** isExecutable
//**************************************************************************
/** Used to check whether the file has execute permissions. Note that this
* method is not supported by JDK 1.5 or lower. Instead, the method will
* return false.
*/
public boolean isExecutable(){
java.io.File File = getFile();
if (File!=null){
//return File.canExecute(); //<--incompatable with JDK 1.5
int javaVersion = javaxt.utils.Java.getVersion();
if (javaVersion<6) return false;
else{
try{
return (Boolean) File.getClass().getMethod("canExecute").invoke(File, null);
}
catch(Exception e){
return false;
}
}
}
else return false;
}
//**************************************************************************
//** isLink
//**************************************************************************
/** Used to determine whether the file is actually a link to another file.
* Returns true for symbolic links, Windows junctions, and Windows
* shortcuts.
*/
public boolean isLink(){
return getLink()!=null;
}
//**************************************************************************
//** getLink
//**************************************************************************
/** Returns the target of a symbolic link, Windows junction, or Windows
* shortcut.
*/
public java.io.File getLink(){
try{
return getFileAttributes().getLink();
}
catch(Exception e){
return null;
}
}
//**************************************************************************
//** Delete File
//**************************************************************************
/** Used to delete the file. Warning: this operation is irrecoverable. */
public boolean delete(){
java.io.File file = getFile();
if (file!=null){
if (file.delete()){
attr=null;
return true;
}
}
return false;
}
//**************************************************************************
//** setBufferSize
//**************************************************************************
/** Used to set the size of the buffer used to read/write bytes. The default
* is 1MB (1,048,576 bytes)
*/
public void setBufferSize(int numBytes){
bufferSize = numBytes;
}
//**************************************************************************
//** Move File
//**************************************************************************
/** Used to move the file to a different directory. If the operation is
* successful, returns a handle to the new file. Otherwise, the original
* file is returned.
*/
public javaxt.io.File moveTo(Directory Destination){
return moveTo(new javaxt.io.File(Destination, getName()), true);
}
//**************************************************************************
//** Move File
//**************************************************************************
/** Used to move the file to a different location. If the operation is
* successful, returns a handle to the new file. Otherwise, the original
* file is returned.
*/
public javaxt.io.File moveTo(javaxt.io.File Destination, boolean Overwrite){
if (Destination.exists()){
if (Overwrite){
Destination.delete();
}
else{
return this;
}
}
java.io.File oldFile = getFile();
java.io.File newFile = Destination.toFile();
newFile.getParentFile().mkdirs();
if (oldFile.renameTo(newFile)){
attr = null;
file = newFile;
init(file.getAbsolutePath());
}
return this;
}
//**************************************************************************
//** Copy File
//**************************************************************************
/** Used to create a copy of this file. Preserves the last modified date
* associated with the source file. Returns true if the file was copied
* successfully.
*/
public boolean copyTo(Directory Destination, boolean Overwrite){
return copyTo(new File(Destination, getName()), Overwrite);
}
//**************************************************************************
//** Copy File
//**************************************************************************
/** Used to create a copy of this file. Preserves the last modified date
* associated with the source file. Returns true if the file was copied
* successfully.
*/
public boolean copyTo(javaxt.io.File Destination, boolean Overwrite){
//Validate Input/Output
java.io.File File = getFile();
if (File.exists()==false) return false;
if (Destination.exists() && Overwrite==false) return false;
if (File.equals(Destination.toFile())) return false;
//Create New Path
Destination.getParentDirectory().create();
//Copy File
ReadableByteChannel inputChannel = null;
WritableByteChannel outputChannel = null;
try{
FileInputStream input = new FileInputStream(File);
FileOutputStream output = new FileOutputStream(Destination.toFile());
inputChannel = Channels.newChannel(input);
outputChannel = Channels.newChannel(output);
final java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocateDirect(bufferSize);
while (inputChannel.read(buffer) != -1) {
buffer.flip();
outputChannel.write(buffer);
buffer.compact();
}
buffer.flip();
while (buffer.hasRemaining()) {
outputChannel.write(buffer);
}
inputChannel.close();
outputChannel.close();
Destination.toFile().setLastModified(File.lastModified());
return true;
}
catch(Exception e){
try{inputChannel.close();}catch(Exception ex){}
try{outputChannel.close();}catch(Exception ex){}
return false;
}
}
//**************************************************************************
//** Rename File
//**************************************************************************
/** Used to rename the file. The existing file name is replaced with a new
* name. Only the file name is affected. The file path remains unchanged.
* This method is therefore different from the java.io.File "renameTo"
* method. If the operation is successful, returns a handle to the new file.
* Otherwise, the original file is returned.
* @param FileName The new file name (including the file extension).
*/
public javaxt.io.File rename(String FileName){
if (FileName!=null){
FileName = FileName.trim();
if (FileName.length()>0){
java.io.File File = getFile();
if (File!=null) {
java.io.File newFile = new java.io.File(getPath() + FileName);
if (File.renameTo(newFile)){
attr = null;
file = newFile;
init(file.getAbsolutePath());
}
}
}
}
return this;
}
//**************************************************************************
//** getBufferedWriter
//**************************************************************************
/** Used to instantiate a BufferedWriter for this file.
*/
public BufferedWriter getBufferedWriter(String charsetName){
try{
java.io.File File = getFile();
attr = null;
File.getParentFile().mkdirs();
if (charsetName==null){
return new BufferedWriter( new FileWriter(File) );
}
else{
return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(File), charsetName));
}
}
catch (Exception e){
return null;
}
}
//**************************************************************************
//** getBufferedReader
//**************************************************************************
/** Used to extract the contents of the file into a BufferedReader.
BufferedReader br = file.getBufferedReader("UTF-8");
String strLine;
while ((strLine = br.readLine()) != null){
System.out.println(strLine);
}
* @return BufferedReader or null
*/
public BufferedReader getBufferedReader(){
java.io.File File = getFile();
if (File.exists()){
try{
//NOTE: FileReader always assumes default encoding is OK!
return new BufferedReader( new FileReader(File) );
}
catch (Exception e){
}
}
return null;
}
//**************************************************************************
//** getBufferedReader
//**************************************************************************
/** Used to extract the contents of the file into a BufferedReader.
BufferedReader br = file.getBufferedReader("UTF-8");
String strLine;
while ((strLine = br.readLine()) != null){
System.out.println (strLine);
}
* WARNING: This method will never throw an error.
*
* @param charsetName Name of the character encoding used to read the file.
* Examples include UTF-8 and ISO-8859-1
*/
public BufferedReader getBufferedReader(String charsetName){
java.io.File File = getFile();
if (File.exists()){
try{
return new java.io.BufferedReader(new java.io.InputStreamReader(this.getInputStream(),charsetName));
}
catch (Exception e){
}
}
return null;
}
//**************************************************************************
//** getBufferedImage
//**************************************************************************
/** Returns the file contents as a BufferedImage. Returns a null if the file
* contents cannot be converted into a BufferedImage.
*/
public java.awt.image.BufferedImage getBufferedImage(){
Image img = getImage();
if (img!=null) return img.getBufferedImage();
return null;
}
//**************************************************************************
//** getImage
//**************************************************************************
/** Returns the file contents as an Image (javaxt.io.Image). Returns a null
* if the file contents cannot be converted into an Image.
*/
public Image getImage(){
java.io.File File = getFile();
if (File.exists()) return new Image(File);
else return null;
}
//**************************************************************************
//** getText
//**************************************************************************
/** Returns the file contents as a String. Returns an empty String if the
* file is empty or the contents cannot be converted to a String.
*/
public String getText(){
try{
return getText("UTF-8");
}
catch(Exception e){}
try{
return getBytes().toString();
}
catch(Exception e){}
return "";
}
//**************************************************************************
//** getText
//**************************************************************************
/** Returns the file contents as a String. Returns an empty String if the
* file is empty or the contents cannot be converted to a String.
* @param charsetName Name of the character encoding used to read the file.
* Examples include UTF-8 and ISO-8859-1
*/
public String getText(String charsetName){
try{
return getBytes().toString(charsetName);
}
catch(Exception e){}
return "";
}
//**************************************************************************
//** getXML
//**************************************************************************
/** Returns the file contents as a XML DOM Document(org.w3c.dom.Document).
* Returns a null if the file contents cannot be converted into a DOM
* Document.
*/
public org.w3c.dom.Document getXML(){
try (FileInputStream is = getInputStream()) {
return javaxt.xml.DOM.createDocument(is);
}
catch(Exception e){
return null;
}
}
//**************************************************************************
//** getJSONObject
//**************************************************************************
/** Returns the file contents as a JSON object (javaxt.json.JSONObject).
* Returns a null if the file contents cannot be converted into a JSON
* object.
*/
public javaxt.json.JSONObject getJSONObject(){
try {
return new javaxt.json.JSONObject(getText());
}
catch(Exception e){
return null;
}
}
//**************************************************************************
//** getJSONArray
//**************************************************************************
/** Returns the file contents as a JSON array (javaxt.json.JSONArray).
* Returns a null if the file contents cannot be converted into a JSON
* array.
*/
public javaxt.json.JSONArray getJSONArray(){
try {
return new javaxt.json.JSONArray(getText());
}
catch(Exception e){
return null;
}
}
//**************************************************************************
//** getBytes
//**************************************************************************
/** Returns the file contents as a ByteArrayOutputStream. Returns a null if
* the file contents cannot be converted to a ByteArrayOutputStream.
*/
public ByteArrayOutputStream getBytes(){
return getBytes(getFile(), bufferSize);
}
private static ByteArrayOutputStream getBytes(java.io.File File, int bufferSize){
if (File.exists()){
try (FileInputStream is = new FileInputStream(File)) {
ByteArrayOutputStream bas = new ByteArrayOutputStream();
byte[] b = new byte[bufferSize];
int x;
while((x=is.read(b,0,bufferSize))>-1) {
bas.write(b,0,x);
}
bas.close();
return bas;
}
catch(Exception e){
}
}
return null;
}
//**************************************************************************
//** checksum
//**************************************************************************
/** Returns a long value representing a cyclic redundancy check
* (CRC-32 checksum) of the file, or -1 if not known.
*/
public long checksum(){
if (!exists()) return -1;
try{
java.util.zip.CRC32 crc = new java.util.zip.CRC32();
crc.update(getBytes().toByteArray());
return crc.getValue();
}
catch(Exception e){
return -1;
}
}
//**************************************************************************
//** getSHA1
//**************************************************************************
/** Returns a string representing the SHA-1 hash for the file.
*/
public String getSHA1() {
return getHash("SHA-1");
}
//**************************************************************************
//** getMD5
//**************************************************************************
/** Returns a string representing the MD5 hash for the file.
*/
public String getMD5() {
return getHash("MD5");
}
//**************************************************************************
//** getHash
//**************************************************************************
private String getHash(String algorithm){
if (!exists()) return null;
try (InputStream input = getInputStream()) {
java.security.MessageDigest md = java.security.MessageDigest.getInstance(algorithm);
byte[] buf = new byte[bufferSize];
int i;
while((i=input.read(buf))!=-1) {
md.update(buf, 0, i);
}
//return new javax.xml.bind.annotation.adapters.HexBinaryAdapter().marshal(md.digest()).toLowerCase();
return printHexBinary(md.digest()).toLowerCase();
}
catch(Exception e){
return null;
}
}
private static final char[] hexCode = "0123456789ABCDEF".toCharArray();
private String printHexBinary(byte[] data) {
if (data==null) return null;
StringBuilder r = new StringBuilder(data.length * 2);
for (byte b : data) {
r.append(hexCode[(b >> 4) & 0xF]);
r.append(hexCode[(b & 0xF)]);
}
return r.toString();
}
//**************************************************************************
//** create
//**************************************************************************
/** Creates a new file if one does not exist.
*/
public void create(){
if (exists()) return;
try {
java.io.File file = getFile();
file.getParentFile().mkdirs();
file.createNewFile();
}
catch (Exception e){}
}
//**************************************************************************
//** write
//**************************************************************************
/** Creates a new file using the given ByteArrayOutputStream. Note that this
* method will convert the ByteArrayOutputStream to a byte array using the
* toByteArray() method which may result in memory issues.
*/
public void write(ByteArrayOutputStream bas){
write(bas.toByteArray());
}
//**************************************************************************
//** write
//**************************************************************************
/** Creates a new file using the given byte array.
*/
public void write(byte[] bytes){
java.io.File File = getFile();
attr = null;
if (File!=null){
FileOutputStream output = null;
try {
File.getParentFile().mkdirs();
output = new FileOutputStream(File);
output.write(bytes);
}
catch (Exception e){}
finally {
try { if (output != null) output.close(); }
catch (Exception e){}
}
}
}
//**************************************************************************
//** write
//**************************************************************************
/** Creates a new file using the given InputStream. Note that the caller is
* responsible for closing the input stream after the method is complete.
*/
public void write(InputStream input){
java.io.File File = getFile();
attr = null;
if (File!=null){
FileOutputStream output = null;
try {
File.getParentFile().mkdirs();
output = new FileOutputStream(File);
byte[] buf = new byte[bufferSize];
int i = 0;
while((i=input.read(buf))!=-1) {
output.write(buf, 0, i);
}
}
catch (Exception e){}
finally {
try { if (output != null) output.close(); }
catch (Exception e){}
}
}
}
//**************************************************************************
//** Write Text
//**************************************************************************
/** Used to write text to a file. Uses UTF-8 character encoding. Use the
* other write method to specify a different character encoding (e.g.
* ISO-8859-1).
*/
public void write(String Text){
write(Text, "UTF-8");
}
//**************************************************************************
//** Write Text
//**************************************************************************
/** Used to write text to a file. Allows users to specify character encoding.
*
* @param charsetName Name of the character encoding used to read the file.
* Examples include UTF-8 and ISO-8859-1. If null, the writer will use the
* default character encoding defined on the host machine.
*/
public void write(String Text, String charsetName){
java.io.File File = getFile();
attr = null;
if (File!=null){
Writer output = null;
try {
File.getParentFile().mkdirs();
if (charsetName==null){
output = new BufferedWriter( new FileWriter(File) );
}
else{
output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(File), charsetName));
}
output.write( Text );
}
catch (Exception e){}
finally {
try { if (output != null) output.close(); }
catch (Exception e){}
}
}
}
//**************************************************************************
//** Write XML
//**************************************************************************
/** Used to write an XML DOM Document to a file.
*/
public void write(org.w3c.dom.Document xml){
write(javaxt.xml.DOM.getText(xml), xml.getXmlEncoding());
}
//**************************************************************************
//** write
//**************************************************************************
/** Used to write a JSON object to a file. Note that the JSON object is
* encoded using UTF-8.
*/
public void write(javaxt.json.JSONObject json){
write(json.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8));
}
//**************************************************************************
//** write
//**************************************************************************
/** Used to write a JSON array to a file. Note that the JSON array is
* encoded using UTF-8.
*/
public void write(javaxt.json.JSONArray arr){
write(arr.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8));
}
//**************************************************************************
//** Write Text
//**************************************************************************
/** Used to write text to a file using an array of strings. A new line is
* created for each entry in the array.
*/
public void write(String[] Content){
java.io.File File = getFile();
attr = null;
if (File!=null){
Writer output = null;
try {
File.getParentFile().mkdirs();
output = new BufferedWriter( new FileWriter(File) );
for (int i=0; i260) return false;
PathToFile = toString();
PathToFile = PathToFile.replace((CharSequence) "\\", "/");
String[] Path = PathToFile.split("/");
String[] arr = new String[]{ "/", "?", "<", ">", "\\", ":", "*", "|", "\"" };
for (int i=0; i getFlags(){
try{
return getFileAttributes().getFlags();
}
catch(Exception e){
return new java.util.HashSet();
}
}
//**************************************************************************
//** getFileAttributes
//**************************************************************************
/** Returns file attributes such as when the file was first created and when
* it was last accessed. File attributes are cached for up to one second.
* This provides users the ability to retrieve multiple attributes at once.
* Without caching, we would have to ping the file system every time we call
* getLastAccessTime(), getLastAccessTime(), getLastWriteTime(), etc. The
* cached attributes are automatically updated when the file is updated or
* deleted by this class.
*/
private FileAttributes getFileAttributes() throws java.io.FileNotFoundException {
if (attr==null) lastAttrUpdate = 0;
if (attr==null || (new java.util.Date().getTime()-lastAttrUpdate)>1000){
try{
//Get file attributes
String pathToFile = toString();
attr = new FileAttributes(pathToFile);
if (attr.isDirectory()) throw new java.io.FileNotFoundException(pathToFile);
//Set lastUpdate (used to cache file attributes)
lastAttrUpdate = new java.util.Date().getTime();
}
catch(java.io.FileNotFoundException e){
attr = null;
throw e;
}
catch(Exception e){
//e.printStackTrace();
attr = null;
}
}
return attr;
}
//**************************************************************************
//** loadDLL
//**************************************************************************
/** Used to load the javaxt-core.dll. Returns a boolean to indicate load
* status. Note that the dll is only loaded once per JVM so it should be
* safe to call this method multiple times.
*/
protected static synchronized boolean loadDLL(){
//Try to load the dll as needed. Update the
if (isWindows){
if (dllLoaded==null){ //haven't tried to load the dll yet...
String jvmPlatform = System.getProperty("os.arch");
String dllName;
if (jvmPlatform.equalsIgnoreCase("x86")){
dllName = "javaxt-core.dll";
}
else if(jvmPlatform.equalsIgnoreCase("amd64")){
dllName = "javaxt-core64.dll";
}
else{
dllLoaded = false;
return dllLoaded;
}
//Find dll entry in the zip/jar file
Jar jar = new Jar(Jar.class);
Jar.Entry entry = jar.getEntry(null, dllName);
long checksum = entry.checksum();
//Construct list of possible file locations for the dll
java.util.ArrayList files = new java.util.ArrayList<>();
files.add(new java.io.File(jar.getFile().getParentFile(), dllName));
javaxt.io.Directory dir = new javaxt.io.Directory(System.getProperty("user.home"));
for (String appDir : new String[]{"AppData\\Local", "Application Data"}){
javaxt.io.Directory d = new javaxt.io.Directory(dir + appDir);
java.io.File f = d.toFile(); //flip to file to prevent infinite recursion
if (f.exists() && f.canWrite()){
dir = d;
break;
}
}
files.add(new java.io.File(dir + "JavaXT\\" + dllName));
//Try to load dll
for (java.io.File dll : files){
if (dll.exists()){
//Check whether the dll equals the jar entry. Extract as needed.
byte[] b = new byte[(int)dll.length()];
java.io.DataInputStream is = null;
try{
is = new java.io.DataInputStream(new FileInputStream(dll));
is.readFully(b, 0, b.length);
is.close();
java.util.zip.CRC32 crc = new java.util.zip.CRC32();
crc.update(b);
if (checksum!=crc.getValue()){
dll.delete();
entry.extractFile(dll);
}
}
catch(Exception e){
try{is.close();}catch(Exception ex){}
}
}
else{
//File does not exist so extract the dll
entry.extractFile(dll);
}
//Load the dll
if (dll.exists()){
try{
System.load(dll.toString());
dllLoaded = true;
break;
}
catch(Throwable t){
String msg = "already loaded in another classloader";
if (t.getMessage().contains(msg)){ //UnsatisfiedLinkError
System.err.println(System.mapLibraryName("javaxt_core") + " " + msg);
}
else{
t.printStackTrace();
}
}
}
}
//Don't update the static variable to give users a chance to fix
//the load error (e.g. manually extract the dll and copy it into
//one of the directories).
//dllLoaded = false;
}
return dllLoaded==null ? false : dllLoaded;
}
else{//not windows...
return false;
}
}
/** Used to determine whether the JVM is running on Mac OS X. */
private static final boolean isOSX =
System.getProperty("os.name").toLowerCase().contains("os x");
private static final boolean isSolaris =
System.getProperty("os.name").toLowerCase().contains("sunos");
/** Used to track load status. Null = no load attempted, True = successfully
* loaded the dll, False = failed to load dll (don't try again). Do not try
* to modify the value directly. Use the loadDLL() method instead.
*/
private static Boolean dllLoaded;
/** JNI entry point to retrieve file attributes. */
private static native long[] GetFileAttributesEx(String lpPathName) throws Exception;
/** JNI entry point to retrieve link target. */
private static native String GetTarget(String lpPathName) throws Exception;
/** JNI entry point to retrieve a list of shared drives on a server. */
protected static native String GetSharedDrives(String serverName) throws Exception;
/** JNI entry point to retrieve a list of network drives mounted to the host. */
protected static native String GetNetworkDrives() throws Exception;
/** JNI entry point to retrieve a list of files and directories. */
protected static native String GetFiles(String lpPathName) throws Exception;
//******************************************************************************
//** FileAttributes Class
//******************************************************************************
/**
* Used to encapsulate extended file attributes. On unix and linux machines,
* this class is used to parse the output from ls. On windows, this class
* uses a JNI to return WIN32_FILE_ATTRIBUTE_DATA:
*
typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
} WIN32_FILE_ATTRIBUTE_DATA;
*
******************************************************************************/
public static class FileAttributes {
private java.util.Date ftCreationTime;
private java.util.Date ftLastAccessTime;
private java.util.Date ftLastWriteTime;
private long size;
private java.util.HashSet flags = new java.util.HashSet();
private java.io.File link;
public FileAttributes(String path) throws java.io.FileNotFoundException, Exception {
if (isWindows){
if (loadDLL()){
//Get attributes
long[] attributes = null;
try{
attributes = GetFileAttributesEx(path);
}
catch(Exception e){
if (!new java.io.File(path).exists()){
throw new java.io.FileNotFoundException(path);
}
else throw e;
}
//Parse dates
java.text.SimpleDateFormat ftFormatter =
new java.text.SimpleDateFormat("yyyyMMddHHmmssSSS");
ftCreationTime = ftFormatter.parse(attributes[1]+"");
ftLastAccessTime = ftFormatter.parse(attributes[2]+"");
ftLastWriteTime = ftFormatter.parse(attributes[3]+"");
//Compute file size using the nFileSizeHigh and nFileSizeLow attributes
//Note that nFileSizeHigh will be zero unless the file size is greater
//than MAXDWORD (4.2 Gig)
long MAXDWORD = 4294967296L;
long nFileSizeHigh = attributes[4];
long nFileSizeLow = attributes[5];
size = (nFileSizeHigh * MAXDWORD) + nFileSizeLow;
//Parse misc file attributes
long dwFileAttributes = attributes[0];
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)
flags.add("READONLY");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN)
flags.add("HIDDEN");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_SYSTEM) == FILE_ATTRIBUTE_SYSTEM)
flags.add("SYSTEM");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
flags.add("DIRECTORY");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE)
flags.add("ARCHIVE");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_DEVICE) == FILE_ATTRIBUTE_DEVICE)
flags.add("DEVICE");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_NORMAL) == FILE_ATTRIBUTE_NORMAL)
flags.add("NORMAL");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_TEMPORARY) == FILE_ATTRIBUTE_TEMPORARY)
flags.add("TEMPORARY");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_SPARSE_FILE) == FILE_ATTRIBUTE_SPARSE_FILE)
flags.add("SPARSE_FILE");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT)
flags.add("REPARSE_POINT");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_COMPRESSED) == FILE_ATTRIBUTE_COMPRESSED)
flags.add("COMPRESSED");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_OFFLINE) == FILE_ATTRIBUTE_OFFLINE)
flags.add("OFFLINE");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) == FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
flags.add("NOT_CONTENT_INDEXED");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_ENCRYPTED) == FILE_ATTRIBUTE_ENCRYPTED)
flags.add("ENCRYPTED");
if (bitand(dwFileAttributes, FILE_ATTRIBUTE_VIRTUAL) == FILE_ATTRIBUTE_VIRTUAL)
flags.add("VIRTUAL");
//Parse symlink
if (flags.contains("REPARSE_POINT")){
link = new java.io.File(GetTarget(path));
}
}
else{
//Failed to load the javaxt-core.dll. Fall back to the java.io.File object.
java.io.File f = new java.io.File(path);
if (!f.exists()) throw new java.io.FileNotFoundException(path);
size = f.length();
ftLastWriteTime = new java.util.Date(f.lastModified());
if (!f.canWrite()) flags.add("READONLY");
if (f.isHidden()) flags.add("HIDDEN");
if (f.isDirectory()) flags.add("DIRECTORY");
}
}
else{//UNIX or LINIX Operating System
java.io.File f = new java.io.File(path);
if (!f.exists()) throw new java.io.FileNotFoundException(path);
//Execute ls command to get last access time and creation time
if (isOSX){
String[] params = new String[]{"ls", "-lauT", path};
javaxt.io.Shell cmd = new javaxt.io.Shell(params);
cmd.run();
java.util.Iterator it = cmd.getOutput().iterator();
ftLastAccessTime = parseOSXDate(it);
params = new String[]{"ls", "-laUT", path};
cmd = new javaxt.io.Shell(params);
cmd.run();
it = cmd.getOutput().iterator();
ftCreationTime = parseOSXDate(it);
}
else if (isSolaris){
String[] params = new String[]{"ls", "-lauE", path};
javaxt.io.Shell cmd = new javaxt.io.Shell(params);
cmd.run();
java.util.Iterator it = cmd.getOutput().iterator();
ftLastAccessTime = parseFullDate(it);
}
else{//Linux (e.g. Ubuntu)
String[] params = new String[]{"ls", "-lau", "--full-time", path};
javaxt.io.Shell cmd = new javaxt.io.Shell(params);
cmd.run();
java.util.Iterator it = cmd.getOutput().iterator();
ftLastAccessTime = parseFullDate(it);
params = new String[]{"ls", "-laU", "--full-time", path};
cmd = new javaxt.io.Shell(params);
cmd.run();
it = cmd.getOutput().iterator();
ftCreationTime = parseFullDate(it);
}
//Set other attributes including last modified date
size = f.length();
ftLastWriteTime = new java.util.Date(f.lastModified());
if (!f.canWrite()) flags.add("READONLY");
if (f.isHidden()) flags.add("HIDDEN");
if (f.isDirectory()) flags.add("DIRECTORY");
try{
if (!f.getCanonicalFile().equals(f.getAbsoluteFile())){
flags.add("REPARSE_POINT");
link = f.getCanonicalFile();
}
}
catch(Exception e){
}
}
//Check whether the file is a Windows shortcut (.lnk file) and parse as needed
if (!isDirectory() && link==null){
if (path.toLowerCase().endsWith(".lnk")){
try{
link = new LnkParser(path).getFile();
}
catch(Exception e){
}
}
}
}
/** Used to extract a date from a ls output using the "--full-time" option. */
private java.util.Date parseFullDate(java.util.Iterator it){
while (it.hasNext()){
String line = it.next();
if (line!=null){
while (line.contains(" ")) line = line.replace(" ", " ");
String[] arr = line.split(" ");
if (arr.length>7){
try{
if (arr[6].length()>8) arr[6] = arr[6].substring(0,8);
String date = arr[5] + " " + arr[6] + " " + arr[7];
return new javaxt.utils.Date(date, "yyyy-MM-dd HH:mm:ss z").getDate();
}
catch(Exception e){
}
}
}
}
return null;
}
/** Used to extract a date from a ls output using the "T" option on OSX. */
private java.util.Date parseOSXDate(java.util.Iterator it){
while (it.hasNext()){
String line = it.next();
if (line!=null){
while (line.contains(" ")) line = line.replace(" ", " ");
String[] arr = line.split(" ");
if (arr.length>10){
try{
String date = arr[5] + " " + arr[6] + " " + arr[7]+ " " + arr[8];
return new javaxt.utils.Date(date, "MMM dd hh:mm:ss yyyy").getDate();
}
catch(Exception e){
}
}
}
}
return null;
}
public long getSize(){
return size;
}
public java.util.Date getCreationTime(){
return ftCreationTime;
}
public java.util.Date getLastAccessTime(){
return ftLastAccessTime;
}
public java.util.Date getLastWriteTime(){
return ftLastWriteTime;
}
public boolean isDirectory(){
return flags.contains("DIRECTORY");
}
public boolean isHidden(){
return flags.contains("HIDDEN");
}
public boolean isReadOnly(){
return flags.contains("READONLY");
}
public java.util.HashSet getFlags(){
return flags;
}
public java.io.File getLink(){
return link;
}
/** A file that is read-only. Applications can read the file, but cannot
* write to it or delete it. This attribute is not honored on directories.
* For more information, see You cannot view or change the Read-only or the
* System attributes of folders in Windows Server 2003, in Windows XP, in
* Windows Vista or in Windows 7.
*/
private static final int FILE_ATTRIBUTE_READONLY = 1;
/** The file or directory is hidden. It is not included in an ordinary
* directory listing.
*/
private static final int FILE_ATTRIBUTE_HIDDEN = 2;
/** A file or directory that the operating system uses a part of, or uses
* exclusively.
*/
private static final int FILE_ATTRIBUTE_SYSTEM = 4;
/** The handle that identifies a directory. */
private static final int FILE_ATTRIBUTE_DIRECTORY = 16;
/** A file or directory that is an archive file or directory. Applications
* typically use this attribute to mark files for backup or removal.
*/
private static final int FILE_ATTRIBUTE_ARCHIVE = 32;
/** This value is reserved for system use. */
private static final int FILE_ATTRIBUTE_DEVICE = 64;
/** A file that does not have other attributes set. This attribute is valid
* only when used alone.
*/
private static final int FILE_ATTRIBUTE_NORMAL = 128;
/** A file that is being used for temporary storage. File systems avoid
* writing data back to mass storage if sufficient cache memory is available,
* because typically, an application deletes a temporary file after the
* handle is closed. In that scenario, the system can entirely avoid writing
* the data. Otherwise, the data is written after the handle is closed.
*/
private static final int FILE_ATTRIBUTE_TEMPORARY = 256;
/** A file that is a sparse file. */
private static final int FILE_ATTRIBUTE_SPARSE_FILE = 512;
/** A file or directory that has an associated reparse point, or a file that
* is a symbolic link.
*/
private static final int FILE_ATTRIBUTE_REPARSE_POINT = 1024;
/** A file or directory that is compressed. For a file, all of the data in
* the file is compressed. For a directory, compression is the default for
* newly created files and subdirectories.
*/
private static final int FILE_ATTRIBUTE_COMPRESSED = 2048;
/** The data of a file is not available immediately. This attribute indicates
* that the file data is physically moved to offline storage. This attribute
* is used by Remote Storage, which is the hierarchical storage management
* software. Applications should not arbitrarily change this attribute.
*/
private static final int FILE_ATTRIBUTE_OFFLINE = 4096;
/** The file or directory is not to be indexed by the content indexing
* service.
*/
private static final int FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192;
/** A file or directory that is encrypted. For a file, all data streams in
* the file are encrypted. For a directory, encryption is the default for
* newly created files and subdirectories.
*/
private static final int FILE_ATTRIBUTE_ENCRYPTED = 16384;
private static final int FILE_ATTRIBUTE_VIRTUAL = 65536; //This value is reserved for system use.
private long bitand(long Number1, long Number2){
try {
return Number1 & Number2;
}
catch (Exception e) {
return -1;
}
}
}// End FileAttributes Class
//******************************************************************************
//** LnkParser Class
//******************************************************************************
/**
* Class used to parse a windows lnk files (aka shortcuts). Credit:
* http://stackoverflow.com/a/672775/
*
******************************************************************************/
private static class LnkParser {
private boolean isDirectory;
private boolean isLocal;
private String real_file;
public LnkParser(String lnk) {
this(new java.io.File(lnk));
}
//**************************************************************************
//** Constructor
//**************************************************************************
/** Instantiates the class by parsing the Windows shortcut (lnk) file.
* @param lnk File representing the full path to the shortcut file.
*/
public LnkParser(java.io.File lnk) {
byte[] link = getBytes(lnk, 1024).toByteArray();
// get the flags byte
byte flags = link[0x14];
// get the file attributes byte
final int file_atts_offset = 0x18;
byte file_atts = link[file_atts_offset];
byte is_dir_mask = (byte)0x10;
if ((file_atts & is_dir_mask) > 0) {
isDirectory = true;
} else {
isDirectory = false;
}
// if the shell settings are present, skip them
final int shell_offset = 0x4c;
final byte has_shell_mask = (byte)0x01;
int shell_len = 0;
if ((flags & has_shell_mask) > 0) {
// the plus 2 accounts for the length marker itself
shell_len = bytes2short(link, shell_offset) + 2;
}
// get to the file settings
int file_start = 0x4c + shell_len;
final int file_location_info_flag_offset_offset = 0x08;
int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset];
isLocal = (file_location_info_flag & 2) == 0;
// get the local volume and local system values
//final int localVolumeTable_offset_offset = 0x0C;
final int basename_offset_offset = 0x10;
final int networkVolumeTable_offset_offset = 0x14;
final int finalname_offset_offset = 0x18;
int finalname_offset = link[file_start + finalname_offset_offset] + file_start;
String finalname = getNullDelimitedString(link, finalname_offset);
if (isLocal) {
int basename_offset = link[file_start + basename_offset_offset] + file_start;
String basename = getNullDelimitedString(link, basename_offset);
real_file = basename + finalname;
} else {
int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start;
int shareName_offset_offset = 0x08;
int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset]
+ networkVolumeTable_offset;
String shareName = getNullDelimitedString(link, shareName_offset);
real_file = shareName + "\\" + finalname;
}
}
private static String getNullDelimitedString(byte[] bytes, int off) {
int len = 0;
// count bytes until the null character (0)
while (true) {
if (bytes[off + len] == 0) {
break;
}
len++;
}
return new String(bytes, off, len);
}
/*
* convert two bytes into a short note, this is little endian because it's
* for an Intel only OS.
*/
private static int bytes2short(byte[] bytes, int off) {
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
public java.io.File getFile(){
return new java.io.File(real_file);
}
}//End LnkParser Inner Class
}//End File Class