package com.limegroup.gnutella;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.limegroup.gnutella.altlocs.AlternateLocation;
import com.limegroup.gnutella.licenses.License;
import com.limegroup.gnutella.settings.SharingSettings;
import com.limegroup.gnutella.tigertree.HashTree;
import com.limegroup.gnutella.tigertree.TigerTreeCache;
import com.limegroup.gnutella.util.CoWList;
import com.limegroup.gnutella.util.I18NConvert;
import com.limegroup.gnutella.xml.LimeXMLDocument;
/**
* This class contains data for an individual shared file. It also provides
* various utility methods for checking against the encapsulated data.<p>
*/
public class FileDesc implements FileDetails {
/**
* Constant for the index of this <tt>FileDesc</tt> instance in the
* shared file data structure.
*/
private final int _index;
/**
* The absolute path for the file.
*/
private final String _path;
/**
* The name of the file, as returned by File.getName().
*/
private final String _name;
/**
* The size of the file.
*/
private final long _size;
/**
* The modification time of the file, which can be updated.
*/
private long _modTime;
/**
* Constant <tt>Set</tt> of <tt>URN</tt> instances for the file. This
* is immutable.
*/
private final Set /* of URNS */ URNS;
/**
* Constant for the <tt>File</tt> instance.
*/
private final File FILE;
/**
* The constant SHA1 <tt>URN</tt> instance.
*/
private final URN SHA1_URN;
/**
* The License, if one exists, for this FileDesc.
*/
private License _license;
/**
* The LimeXMLDocs associated with this FileDesc.
*/
private final List /* of LimeXMLDocument */ _limeXMLDocs = new CoWList(CoWList.ARRAY_LIST);
/**
* The number of hits this file has recieved.
*/
private int _hits;
/**
* The number of times this file has had attempted uploads
*/
private int _attemptedUploads;
/**
* The number of times this file has had completed uploads
*/
private int _completedUploads;
/**
* Constructs a new <tt>FileDesc</tt> instance from the specified
* <tt>File</tt> class and the associated urns.
*
* @param file the <tt>File</tt> instance to use for constructing the
* <tt>FileDesc</tt>
* @param urns the URNs to associate with this FileDesc
* @param index the index in the FileManager
*/
public FileDesc(File file, Set urns, int index) {
if (file == null) throw new NullPointerException("cannot create a FileDesc with a null File");
if (index < 0) throw new IndexOutOfBoundsException("negative index (" + index + ") not permitted in FileDesc");
if (urns == null) throw new NullPointerException("cannot create a FileDesc with a null URN Set");
FILE = file;
_index = index;
_name = I18NConvert.instance().compose(FILE.getName());
_path = FILE.getAbsolutePath();
_size = FILE.length();
_modTime = FILE.lastModified();
URNS = Collections.unmodifiableSet(urns);
SHA1_URN = extractSHA1();
if (SHA1_URN == null) throw new IllegalArgumentException("no SHA1 URN");
_hits = 0; // Starts off with 0 hits
}
/**
* Returns whether or not this <tt>FileDesc</tt> has any urns.
*
* @return <tt>true</tt> if this <tt>FileDesc</tt> has urns,
* <tt>false</tt> otherwise
*/
public boolean hasUrns() {
return !URNS.isEmpty();
}
/**
* Returns the index of this file in our file data structure.
*
* @return the index of this file in our file data structure
*/
public int getIndex() {
return _index;
}
/**
* Returns the size of the file on disk, in bytes.
*
* @return the size of the file on disk, in bytes
*/
public long getFileSize() {
return _size;
}
/**
* Returns the name of this file.
*
* @return the name of this file
*/
public String getFileName() {
return _name;
}
/**
* Returns the last modification time for the file according to this
* <tt>FileDesc</tt> instance.
*
* @return the modification time for the file
*/
public long lastModified() {
return _modTime;
}
/**
* Extracts the SHA1 URN from the set of urns.
*/
private URN extractSHA1() {
for (Iterator iter = URNS.iterator(); iter.hasNext(); ) {
URN urn = (URN)iter.next();
if (urn.isSHA1()) return urn;
}
// this should never happen!!
return null;
}
/**
* Returns the <tt>File</tt> instance for this <tt>FileDesc</tt>.
*
* @return the <tt>File</tt> instance for this <tt>FileDesc</tt>
*/
public File getFile() {
return FILE;
}
public URN getSHA1Urn() {
return SHA1_URN;
}
/**
* Returns a new <tt>Set</tt> instance containing the <tt>URN</tt>s
* for the this <tt>FileDesc</tt>. The <tt>Set</tt> instance
* returned is immutable.
*
* @return a new <tt>Set</tt> of <tt>URN</tt>s for this
* <tt>FileDesc</tt>
*/
public Set getUrns() {
return URNS;
}
/**
* Returns the absolute path of the file represented wrapped by this
* <tt>FileDesc</tt>.
*
* @return the absolute path of the file
*/
public String getPath() {
return FILE.getAbsolutePath();
}
/**
* Adds a LimeXMLDocument to this FileDesc.
*/
public void addLimeXMLDocument(LimeXMLDocument doc) {
_limeXMLDocs.add(doc);
doc.setIdentifier(FILE);
if (doc.isLicenseAvailable()) _license = doc.getLicense();
}
/**
* Replaces one LimeXMLDocument with another.
*/
public boolean replaceLimeXMLDocument(LimeXMLDocument oldDoc, LimeXMLDocument newDoc) {
synchronized (_limeXMLDocs) {
int index = _limeXMLDocs.indexOf(oldDoc);
if (index == -1) return false;
_limeXMLDocs.set(index, newDoc);
}
newDoc.setIdentifier(FILE);
if (newDoc.isLicenseAvailable()) _license = newDoc.getLicense();
else if (_license != null && oldDoc.isLicenseAvailable()) _license = null;
return true;
}
/**
* Removes a LimeXMLDocument from the FileDesc.
*/
public boolean removeLimeXMLDocument(LimeXMLDocument toRemove) {
if (!_limeXMLDocs.remove(toRemove)) return false;
if (_license != null && toRemove.isLicenseAvailable()) _license = null;
return true;
}
/**
* Returns the LimeXMLDocuments for this FileDesc.
*/
public List getLimeXMLDocuments() {
return _limeXMLDocs;
}
public LimeXMLDocument getXMLDocument() {
List docs = getLimeXMLDocuments();
return docs.isEmpty() ? null : (LimeXMLDocument)docs.get(0);
}
/**
* Determines if a license exists on this FileDesc.
*/
public boolean isLicensed() {
return _license != null;
}
/**
* Returns the license associated with this FileDesc.
*/
public License getLicense() {
return _license;
}
/**
* Determine whether or not the given <tt>URN</tt> instance is
* contained in this <tt>FileDesc</tt>.
*
* @param urn the <tt>URN</tt> instance to check for
* @return <tt>true</tt> if the <tt>URN</tt> is a valid <tt>URN</tt>
* for this file, <tt>false</tt> otherwise
*/
public boolean containsUrn(URN urn) {
return URNS.contains(urn);
}
/**
* Returns TIGER_TREE
* @return the <tt>TigerTree</tt> this class holds
*/
public HashTree getHashTree() {
return TigerTreeCache.instance().getHashTree(this);
}
/**
* Increase & return the new hit count.
* @return the new hit count
*/
public int incrementHitCount() {
return ++_hits;
}
/**
* @return the current hit count
*/
public int getHitCount() {
return _hits;
}
/**
* Increase & return the new attempted uploads
* @return the new attempted upload count
*/
public int incrementAttemptedUploads() {
return ++_attemptedUploads;
}
/**
* @return the current attempted uploads
*/
public int getAttemptedUploads() {
return _attemptedUploads;
}
/**
* Increase & return the new completed uploads
* @return the new completed upload count
*/
public int incrementCompletedUploads() {
return ++_completedUploads;
}
/**
* @return the current completed uploads
*/
public int getCompletedUploads() {
return _completedUploads;
}
/**
* Opens an input stream to the <tt>File</tt> instance for this
* <tt>FileDesc</tt>.
*
* @return an <tt>InputStream</tt> to the <tt>File</tt> instance
* @throws <tt>FileNotFoundException</tt> if the file represented
* by the <tt>File</tt> instance could not be found
*/
public InputStream createInputStream() throws FileNotFoundException {
return new BufferedInputStream(new FileInputStream(FILE));
}
// overrides Object.toString to provide a more useful description
public String toString() {
return
"FileDesc:\r\n" +
"name: " + _name + "\r\n" +
"index: " + _index + "\r\n" +
"path: " + _path + "\r\n" +
"size: " + _size + "\r\n" +
"modTime: " + _modTime + "\r\n" +
"File: " + FILE + "\r\n" +
"urns: " + URNS + "\r\n" +
"docs: " + _limeXMLDocs;
}
public InetSocketAddress getSocketAddress() {
// TODO maybe cache this, even statically
try {
return new InetSocketAddress(InetAddress.getByAddress(RouterService.getAcceptor().getAddress(true)), RouterService.getAcceptor().getPort(true));
} catch (UnknownHostException e) {}
return null;
}
public boolean isFirewalled() {
return !RouterService.acceptedIncomingConnection();
}
/**
* Determines if this FileDesc has been validated.
*/
public boolean isVerified() {
return RouterService.getContentManager().isVerified(SHA1_URN);
}
}