/*
* Copyright 1999-2006 University of Chicago
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dcache.ftp.client;
import org.dcache.ftp.client.exception.FTPException;
import java.util.StringTokenizer;
/**
* Represents the properties of a remote file
* such as size, name, modification date and time, etc.
* Can represent a regular file as well as a directory
* or a soft link.
*/
public class FileInfo
{
public static final byte UNKNOWN_TYPE = 0;
public static final byte FILE_TYPE = 1;
public static final byte DIRECTORY_TYPE = 2;
public static final byte SOFTLINK_TYPE = 3;
public static final byte DEVICE_TYPE = 4;
public static final String UNKNOWN_STRING = "?";
public static final int UNKNOWN_NUMBER = -1;
private long size = UNKNOWN_NUMBER;
private String name = UNKNOWN_STRING;
private String date = UNKNOWN_STRING;
private String time = UNKNOWN_STRING;
private byte fileType;
private int mode = 0;
/**
* Used internally by the FTPClient.
*/
public FileInfo()
{
}
/**
* Parses the file information from one line of response to
* the FTP LIST command. Note: There is no commonly accepted
* standard for the format of LIST response.
* This parsing method only accepts
* the most common Unix file listing formats:
* System V or Berkeley (BSD) 'ls -l'
*
* @param unixListReply a single line from ls -l command
* @see #parseUnixListReply(String reply)
*/
public FileInfo(String unixListReply)
throws FTPException
{
parseUnixListReply(unixListReply);
}
/**
* Given a line of reply received as the result of "LIST" command,
* this method will set all the attributes(name,size,time,date and file type)
* of the named file. This method requires the reply to be in
* FTP server format, corresponding to either Unix System V or
* Berkeley (BSD) output of 'ls -l'. For example,
* <pre>drwxr-xr-x 2 guest other 1536 Jan 31 15:15 run.bat</pre>
* or
* <pre>-rw-rw-r-- 1 globus 117579 Nov 29 13:24 AdGriP.pdf</pre>
* If the entry corresponds to a device file, only the file type
* will be set and the other parameters will be set to UNKNOWN.
*
* @param reply reply of FTP server for "dir" command.
* @throws FTPException if unable to parse the reply
*/
//protected void parseUnixListReply(String reply)
public void parseUnixListReply(String reply)
throws FTPException
{
if (reply == null) return;
StringTokenizer tokens = new StringTokenizer(reply);
String token, previousToken;
int numTokens = tokens.countTokens();
if (numTokens < 8) {
throw new FTPException(FTPException.UNSPECIFIED,
"Invalid number of tokens in the list reply [" +
reply + "]");
}
token = tokens.nextToken();
// permissions
switch (token.charAt(0)) {
case 'd':
setFileType(DIRECTORY_TYPE);
break;
case '-':
setFileType(FILE_TYPE);
break;
case 'l':
setFileType(SOFTLINK_TYPE);
break;
case 'c':
case 'b':
// do not try to parse device entries;
// they aren't important anyway
setFileType(DEVICE_TYPE);
return;
default:
setFileType(UNKNOWN_TYPE);
}
try {
for (int i = 1; i <= 9; i++) {
if (token.charAt(i) != '-') {
mode += 1 << (9 - i);
}
}
} catch (IndexOutOfBoundsException e) {
throw new FTPException(FTPException.UNSPECIFIED,
"Could not parse access permission bits");
}
// ??? can ignore
tokens.nextToken();
// next token is the owner
tokens.nextToken();
// In ls from System V, next token is the group
// In ls from Berkeley (BSD), group token is missing
previousToken = tokens.nextToken();
// size
token = tokens.nextToken();
/*
* if the group is missing this will try to parse the date field
* as an integer and will fail. if so, then the previous field is the size field
* and the current token is part of the date.
*/
try {
setSize(Long.parseLong(token));
token = null;
} catch (NumberFormatException e) {
// this might mean that the group is missing
// and this token is part of date.
try {
setSize(Long.parseLong(previousToken));
} catch (NumberFormatException ee) {
throw new FTPException(FTPException.UNSPECIFIED,
"Invalid size number in the ftp reply [" +
previousToken + ", " + token + "]");
}
}
// date - two fields together
if (token == null) {
token = tokens.nextToken();
}
String month = token;
setDate(token + " " + tokens.nextToken());
//next token is either date or time
token = tokens.nextToken();
this.setTime(token);
// this is to handle spaces in the filenames
// as well filenames with dates in them
int ps = reply.indexOf(month);
if (ps == -1) {
// this should never happen
throw new FTPException(FTPException.UNSPECIFIED,
"Could not find date token");
} else {
ps = reply.indexOf(this.time, ps + month.length());
if (ps == -1) {
// this should never happen
throw new FTPException(FTPException.UNSPECIFIED,
"Could not find time token");
} else {
this.setName(reply.substring(1 + ps + this.time.length()));
}
}
}
// --------------------------------
/**
* Sets the file size.
*
* @param size size of the file
*/
public void setSize(long size)
{
this.size = size;
}
/**
* Sets the file name.
*
* @param name name of the file.
*/
public void setName(String name)
{
this.name = name;
}
/**
* Sets the file date.
*
* @param date date of the file.
*/
public void setDate(String date)
{
this.date = date;
}
/**
* Sets modification time of the file.
*
* @param time time of the file.
*/
public void setTime(String time)
{
this.time = time;
}
/**
* Sets the file type.
*
* @param type one of the file types,
* e.g. FILE_TYPE, DIRECTORY_TYPE
*/
public void setFileType(byte type)
{
this.fileType = type;
}
// ---------------------------------
/**
* Returns size of the file.
*
* @return size of the file in bytes
*/
public long getSize()
{
return size;
}
/**
* Returns name of the file.
*
* @return name of the file.
*/
public String getName()
{
return name;
}
/**
* Returns date of the file.
*
* @return date of the file.
*/
public String getDate()
{
return date;
}
/**
* Returns modification time of the file.
*
* @return time of the file.
*/
public String getTime()
{
return time;
}
/**
* Tests if this file is a file.
*
* @return true if this represents a file,
* otherwise, false.
*/
public boolean isFile()
{
return (fileType == FILE_TYPE);
}
/**
* Tests if this file is a directory.
*
* @return true if this reprensets a directory,
* otherwise, false.
*/
public boolean isDirectory()
{
return (fileType == DIRECTORY_TYPE);
}
/**
* Tests if this file is a softlink.
*
* @return true if this reprensets a softlink,
* otherwise, false.
*/
public boolean isSoftLink()
{
return (fileType == SOFTLINK_TYPE);
}
/**
* Tests if this file is a device.
*/
public boolean isDevice()
{
return (fileType == DEVICE_TYPE);
}
public int getMode()
{
return mode;
}
public String getModeAsString()
{
StringBuilder modeStr = new StringBuilder();
for (int j = 2; j >= 0; j--) {
int oct = 0;
for (int i = 2; i >= 0; i--) {
if ((mode & (1 << j * 3 + i)) != 0) {
oct += (int) Math.pow(2, i);
}
}
modeStr.append(String.valueOf(oct));
}
return modeStr.toString();
}
public boolean userCanRead()
{
return ((mode & (1 << 8)) != 0);
}
public boolean userCanWrite()
{
return ((mode & (1 << 7)) != 0);
}
public boolean userCanExecute()
{
return ((mode & (1 << 6)) != 0);
}
public boolean groupCanRead()
{
return ((mode & (1 << 5)) != 0);
}
public boolean groupCanWrite()
{
return ((mode & (1 << 4)) != 0);
}
public boolean groupCanExecute()
{
return ((mode & (1 << 3)) != 0);
}
public boolean allCanRead()
{
return ((mode & (1 << 2)) != 0);
}
public boolean allCanWrite()
{
return ((mode & (1 << 1)) != 0);
}
public boolean allCanExecute()
{
return ((mode & (1 << 0)) != 0);
}
// --------------------------------
public String toString()
{
StringBuilder buf = new StringBuilder();
buf.append("FileInfo: ");
buf.append(getName()).append(" ");
buf.append(getSize()).append(" ");
buf.append(getDate()).append(" ");
buf.append(getTime()).append(" ");
switch (fileType) {
case DIRECTORY_TYPE:
buf.append("directory");
break;
case FILE_TYPE:
buf.append("file");
break;
case SOFTLINK_TYPE:
buf.append("softlink");
break;
default:
buf.append("unknown type");
}
buf.append(" ").append(getModeAsString());
return buf.toString();
}
}