Library.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
org.xnap.io |
![]() |
![]() |
XNap 3 |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/*
* XNap - A P2P framework and client.
*
* See the file AUTHORS for copyright information.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.xnap.io;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.xnap.XNap;
import org.xnap.event.ListListener;
import org.xnap.event.ListSupport;
import org.xnap.event.StatusListener;
import org.xnap.util.FileHelper;
import org.xnap.util.Preferences;
import org.xnap.util.SearchTree;
public class Library implements PropertyChangeListener {
//--- Constant(s) ---
/**
* The name of the file where the library is stored.
*/
public static String FILENAME = FileHelper.getHomeDir() + "library";
/**
* The current version of the library.
*/
public static int VERSION = 1;
// --- Data Field(s) ---
private static Logger logger = Logger.getLogger(Library.class);
private static Library singleton = new Library();
private Preferences prefs = Preferences.getInstance();
/**
* Maps absolute filename to repository index.
*/
private Hashtable indexByFile = new Hashtable();
/**
* Contains all files that have been deleted from disk.
*/
private HashSet deletedFiles;
private int totalCount = 0;
private int sharedCount = 0;
private Thread runner;
private Object updateLock = new Object();
private boolean updatePending = false;
private boolean updateRunning = false;
private ArrayList files = new ArrayList();
private SearchTree st = new SearchTree();
private Vector miProviders = new Vector();
private StatusListener listener;
private ListSupport listeners = new ListSupport(this);
private boolean isRead = false;
// --- Constructor(s) ---
public Library()
{
prefs.addPropertyChangeListener("libraryDirs", this);
prefs.addPropertyChangeListener("uploadDirs", this);
updateLater(true);
}
// --- Method(s) ---
public static Library getInstance()
{
return singleton;
}
/**
* <code>Listener</code> is notified when a new file is
* added.
*/
public void addListListener(ListListener listener)
{
listeners.addListListener(listener);
}
/**
* <code>Provider</code> is notified when a file is added or not uptodate.
*/
public void addMetaInfoProvider(MetaInfoProvider provider)
{
miProviders.add(provider);
}
/**
* Returns a file by index. It is save to call this function with
* index <= size() because the library never shrinks.
*
* @return null, if the file was removed from library; the file
* located at index, otherwise */
public synchronized MetaInfoFile get(int index)
{
return (MetaInfoFile)files.get(index);
}
/**
* Returns a MetaInfoFile object if file is part of library.
*
* @return null, if the file is not part of library; the file,
* otherwise
*/
public synchronized MetaInfoFile get(File file)
{
int i = indexOf(file);
return (i != -1) ? get(i) : null;
}
/**
* Returns file at <code>index</code> and compares it to the given
* filename. If filenames match returns the file.
*
* @return null, if file was not found; the file, otherwise */
public synchronized MetaInfoFile get(String filename, int index)
{
if (index >= 0 && index < files.size()) {
MetaInfoFile file = (MetaInfoFile)get(index);
if (file != null && file.getName().equals(filename)) {
return file;
}
}
return null;
}
public synchronized int indexOf(File file)
{
Integer i = (Integer)indexByFile.get(file);
return (i != null) ? i.intValue() : -1;
}
/**
* Returns true, if file is part of repository and shared. */
public synchronized boolean isShared(File file)
{
int index = indexOf(file);
return (index != -1) ? ((MetaInfoFile)get(index)).isShared() : false;
}
public boolean isRead()
{
return isRead;
}
/**
* Returns false, if <code>file</code> is the incomplete folder
* or its name starts with a dot.
*/
public boolean isPartOfRepository(File file)
{
if (file.getName().startsWith(".")) {
return false;
}
else if (file.isDirectory()) {
return !file.equals(new File(prefs.getIncompleteDir()));
}
return true;
}
public boolean isUpdateRunning()
{
synchronized (updateLock) {
return updateRunning;
}
}
public void propertyChange(PropertyChangeEvent e)
{
updateLater();
}
/**
* Removes <code>listener</code>.
*/
public void removeListListener(ListListener listener)
{
listeners.removeListListener(listener);
}
/**
* Removes <code>provider</code>.
*/
public void removeMetaInfoProvider(MetaInfoProvider provider)
{
miProviders.remove(provider);
}
/**
* @return always returns a valid array
*/
public MetaInfoFile[] search(String searchText)
{
if (st == null) {
return new MetaInfoFile[0];
}
HashSet results = st.search(searchText);
return (results != null)
? (MetaInfoFile[])results.toArray(new MetaInfoFile[0])
: null;
}
public File[] search(String searchText, long filesize)
{
if (st == null) {
return new File[0];
}
HashSet results = st.search(searchText);
if (results != null) {
for (Iterator i = results.iterator(); i.hasNext();) {
if (((File)i.next()).length() != filesize) {
i.remove();
}
}
return (File[])results.toArray(new File[0]);
}
return null;
}
public synchronized int size()
{
return files.size();
}
/**
* Updates the repository.
*/
public void updateLater()
{
updateLater(false);
}
public synchronized void setStatusListener(StatusListener newValue)
{
listener = newValue;
updateStatus();
}
public synchronized void updateStatus()
{
if (listener != null) {
listener.setStatus(XNap.tr("{0} of {1} {2}",
new Integer(sharedCount),
new Integer(totalCount),
(isUpdateRunning())
? XNap.tr("updating")
: XNap.tr("shared")));
}
}
/**
* Optionaly reads the repository and then always updates the repository.
*/
private void updateLater(boolean read)
{
updatePending = true;
synchronized (updateLock) {
if (updateRunning) {
return;
}
updateRunning = true;
}
runner = new Thread(new UpdateRunner(read), "UpdateRepository");
runner.start();
}
private void read()
{
logger.debug("reading library from " + FILENAME);
ObjectInputStream in = null;
try {
in = new ObjectInputStream(new FileInputStream(FILENAME));
int v = in.readInt();
logger.debug("current version: " + VERSION
+ ", repository version: " + v);
if (v < VERSION) {
logger.debug("discarding old repository");
return;
}
int size = in.readInt();
if (size >= 0) {
logger.debug("allocating " + size + " items");
files.ensureCapacity(size);
for (int i = 0; i < size; i++) {
try {
Object o = in.readObject();
if (o != null && o instanceof MetaInfoFile) {
MetaInfoFile file = (MetaInfoFile)o;
addFile(file, file.isShared());
}
}
catch (IOException e) {
throw(e);
}
catch (Exception e) {
logger.error("error reading library at item "
+ i + "/" + size, e);
}
}
}
}
catch(IOException e) {
logger.debug("error reading library file", e);
logger.info(XNap.tr("Could not read library file {0}.", FILENAME));
}
finally {
if (in != null) {
try {
in.close();
}
catch(IOException e) {
}
}
}
}
/**
* Writes repository contents to disk.
*/
private void write()
{
logger.debug("writing library to " + FILENAME);
ObjectOutputStream out = null;
try {
out = new ObjectOutputStream(new FileOutputStream(FILENAME));
out.writeInt(VERSION);
out.writeInt(totalCount);
for (Iterator i = files.iterator(); i.hasNext();) {
Object o = i.next();
if (o != null) {
out.writeObject(o);
}
}
}
catch(IOException e) {
logger.debug("error writing library file", e);
logger.info(XNap.tr("Could not write library file {0}.", FILENAME));
}
finally {
if (out != null) {
try {
out.close();
}
catch(IOException e) {
}
}
}
}
/**
* Adds new files, removes old files and writes the repository to disk.
*/
private void update()
{
logger.debug("updating library");
deletedFiles = new HashSet();
// mark all files for deletion
for (Iterator i = files.iterator(); i.hasNext();) {
File f = (File)i.next();
if (f != null) {
deletedFiles.add(f);
}
}
String[] sharedDirs = prefs.getUploadDirs();
HashSet sharedDirsSet = new HashSet(Arrays.asList(sharedDirs));
// add library dirs that are not shared
String[] dirs = prefs.getLibraryDirs();
for (int i = 0; i < dirs.length; i++) {
addDirectory(new File(dirs[i]), false, sharedDirsSet);
}
// add shared files (updates shared flag if already in library)
for (int i = 0; i < sharedDirs.length; i++) {
addDirectory(new File(sharedDirs[i]), true, null);
}
for (Iterator i = deletedFiles.iterator(); i.hasNext();) {
removeFile((MetaInfoFile)i.next());
}
deletedFiles = null;
write();
}
/**
* Adds <code>file</code> recursively.
*
* <p>Only invoked from {@link #update()}.
*/
private void addDirectory(File file, boolean shared,
HashSet excludeDirs)
{
if ((excludeDirs != null
&& excludeDirs.contains(file.getAbsolutePath()))
|| !isPartOfRepository(file)) {
return;
}
if (file.isDirectory()) {
File list[] = file.listFiles();
if (list != null) {
for (int i = 0; i < list.length; i++) {
addDirectory(list[i], shared, excludeDirs);
}
}
}
else if (file.isFile() && file.canRead()) {
addFile(file, shared);
}
}
/**
* <p>Only invoked from {@link #addDirectory(File, boolean, HashSet)}.
*/
private void addFile(File file, boolean shared)
{
int index = indexOf(file);
if (index == -1) {
if (!(file instanceof MetaInfoFile)) {
file = new MetaInfoFile(file);
}
// update
updateFile((MetaInfoFile)file, shared);
// add new file
files.add(file);
indexByFile.put(file, new Integer(files.size() - 1));
totalCount++;
if (shared) {
sharedCount++;
}
listeners.fireItemAdded(file);
if (totalCount % 16 == 0) {
updateStatus();
}
if (st != null) {
st.add(file.getAbsolutePath(), file);
}
}
else {
// update
updateFile(get(index), shared);
}
}
/**
* Adds file if not already present.
*
* <p>Only invoked from {@link #addFile(File,boolean)}.
*/
private void updateFile(MetaInfoFile file, boolean shared)
{
if (deletedFiles != null) {
deletedFiles.remove(file);
}
file.setShared(shared);
if (!file.isUpToDate()) {
Object[] l = miProviders.toArray();
if (l != null) {
for (int i = l.length - 1; i >= 0; i--) {
synchronized (file) {
((MetaInfoProvider)l[i]).handle(file);
}
}
}
file.setLastUpdate(System.currentTimeMillis());
}
}
/**
* <p>Only invoked from {@link #update()}.
*/
private void removeFile(MetaInfoFile file)
{
int index = indexOf(file);
logger.debug("removed (" + index + ") " + file);
// don't acctually remove file to not mess up indicies
files.set(index, null);
indexByFile.remove(file);
if (file.isShared()) {
sharedCount--;
}
// FIX: remove file from search tree here
totalCount--;
if (totalCount % 16 == 0) {
updateStatus();
}
listeners.fireItemRemoved(file);
}
//--- Inner Class(es) ---
private class UpdateRunner implements Runnable
{
boolean read;
public UpdateRunner(boolean read)
{
this.read = read;
}
public void run()
{
updateStatus();
if (read) {
read();
isRead = true;
}
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (updatePending) {
updatePending = false;
update();
}
synchronized (updateLock) {
updateRunning = false;
}
updateStatus();
}
}
}
The table below shows all metrics for Library.java.




