package com.simonepezzano.hshare;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.GregorianCalendar;
import java.util.Iterator;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
/**
* Everything related to the activities on the file system
* @author Simone Pezzano
*
*/
public class HFiles {
public static final String HF_FILENAME = "HShare_files.xml";
private static HFiles instance;
private File hfile;
private Document current = null;
private HFiles(){
hfile = new File(Conf.getInstance().WORKSPACE_DIR.toURI().resolve(HF_FILENAME));
}
public static HFiles getInstance(){
if (instance == null)
instance = new HFiles();
return instance;
}
/**
* @return the document with the file listing
* @throws DocumentException
*/
public Document getDocument() throws DocumentException{
SAXReader sr = new SAXReader();
if(!hfile.exists())
try {
createDocument();
} catch (IOException e) {
HLog.doclogger.fatal("Unable to create new HFiles document",e);
}
if(current == null)
current = sr.read(hfile);
return current;
}
/**
* Creates the document for the file listing in case it does not exist
* @throws IOException
*/
private void createDocument() throws IOException{
current = DocumentHelper.createDocument();
current.addElement("HFiles");
saveHFiles();
}
/**
* Saves the file listing
* @throws IOException
*/
public void saveHFiles() throws IOException{
XMLWriter wr = new XMLWriter(new FileWriter(hfile));
wr.write(current);
wr.close();
}
/**
* Checks the integrity of the tree, adds the missing files, removes the descriptors of the
* files that are no longer on the FS
* @param root the root node to start the check from
* @throws DocumentException
*/
@SuppressWarnings("unchecked")
public void checkTree(Element root) throws DocumentException{
Iterator<Element> its = root.elements().iterator();
while(its.hasNext()){
Element el = its.next();
if(Statics.isADir(el)){
if(!doesDirExist(el))
el.detach();
else{
addMissingFiles(el);
checkTree(el);
}
}
if(Statics.isAFile(el)){
if(!doesFileExist(el))
el.detach();
}
}
}
/**
* Checks the integrity of the tree, adds the missing files, removes the descriptors of the
* files that are no longer on the FS
* @throws DocumentException
*/
public void checkTree() throws DocumentException{
checkTree(getDocument().getRootElement());
}
/**
* Given an HShare file descritor, checks if the file exists on the file system
* @param el an HShare file descriptor
* @return true if the file exists
*/
private boolean doesFileExist(Element el){
return new File(Statics.toURI(el.getParent().attributeValue("path"),el.getStringValue())).exists();
}
/**
* Given an HShare directory descriptor, checks if the directory exists on the file system
* @param el an HShare directory descriptor
* @return true if the directory exists
*/
private boolean doesDirExist(Element el){
return new File(el.attributeValue("path")).exists();
}
/**
* Checks if the directory represented by the given path has already been indexed
* @param dir a path to a directory
* @return true if the directory has already been listed
* @throws DocumentException
*/
private boolean isDirAlreadyListed(String dir) throws DocumentException{
return getDocument().selectSingleNode("//dir[@path=\""+dir+"\"]")!=null;
}
/**
* Adds a path to the HShare listing
* @param path a valid path
* @throws DocumentException
* @throws IOException
*/
public void add(String path) throws DocumentException, IOException{
if(isDirAlreadyListed(path)){
refreshDir(path);
return;
}
addDir(path);
}
/**
* Given the ID of a file, it returns the HShare file descriptor
* @param id a valid HShare ID
* @return an HShare file descriptor
* @throws DocumentException
*/
public Element getElementById(String id) throws DocumentException{
return (Element) getDocument().selectSingleNode("//file[@id=\""+id+"\"]");
}
/**
* Given the ID of a file, it returns a FS file descriptor for that file
* @param id a valid HShare ID
* @return a FS file descriptor
* @throws DocumentException
*/
public File getFileById(String id) throws DocumentException{
Element fd = getElementById(id);
return getFileByElement(fd);
}
/**
* Given an HShare file descriptor, it returns an FS file descriptor
* @param file a valid HShare file descriptor
* @return a FS file descriptor
*/
public File getFileByElement(Element file){
return new File (Statics.toURI(getDirPathByFileElement(file),file.getStringValue()));
}
/**
* Give an HShare directory descriptor, it returns an FS file descriptor for the directory
* @param el a valid HShare directory descriptor
* @return a FS file descriptor for the directory
*/
public File getDirByElement(Element el){
return new File(el.attributeValue("path"));
}
/**
* Given an HShare file descriptor, it returns its directory path
* @param fd a valid HShare file descriptor
* @return the path for the file's directory
*/
public String getDirPathByFileElement(Element fd){
return fd.getParent().attributeValue("path");
}
/**
* Adds a directory and all its inner items to a directory
* @param path a path to a directory
* @return the HShare directory descriptor just created
* @throws DocumentException
* @throws IOException
*/
public Element addDir(String path) throws DocumentException, IOException{
Element dir = getDocument().getRootElement().addElement("dir").addAttribute("path", path);
try{
String id = Statics.MD5(path+String.valueOf(GregorianCalendar.getInstance().getTimeInMillis()));
dir.addAttribute("id", id);
}catch(Exception e){e.printStackTrace();}
File[] items = new File(path).listFiles();
for(int i=0;i<items.length;i++)
if(items[i].isFile())
addFile(dir,items[i].getName());
else
if(items[i].isDirectory() && !items[i].getName().startsWith("."))
addDir(items[i].getAbsolutePath());
saveHFiles();
return dir;
}
/**
* Given an HShare directory descriptor, it removes it from the share list
* @param dir a valid HShare directory descriptor
* @throws IOException
*/
public void removeDir(Element dir) throws IOException{
dir.detach();
}
/**
* Given an HShare directory descriptor and a filename, it adds a new file to tree
* @param parent a valid HShare directory descriptor
* @param fn a filename
* @return the newly created element
*/
public Element addFile(Element parent,String fn){
Element file = parent.addElement("file");
try{
String id = Statics.MD5(fn+String.valueOf(GregorianCalendar.getInstance().getTimeInMillis()));
file.addAttribute("id", id);
file.setText(fn);
}catch(Exception e){e.printStackTrace();}
return file;
}
/**
* Given a path to a directory, it refreshes its content, checking for new or missing items
* @param path a path do a directory. This is a wrapper for checkTree
* @throws DocumentException
*/
public void refreshDir(String path) throws DocumentException{
Element dir = (Element) getDocument().selectSingleNode("//dir[@path=\""+path+"\"]");
checkTree(dir);
}
/**
* Given an HShare directory descriptor, it adds to the listing the files that are present
* in the FS but not in the share
* @param dir a valid HShare directory descriptor
*/
public void addMissingFiles(Element dir){
File[] files = getDirByElement(dir).listFiles();
for(int i=0;i<files.length;i++){
if(files[i].isFile()&&!isFileAlreadyListed(files[i], dir))
addFile(dir,files[i].getName());
}
}
/**
* Given an HShare directory descriptor, it returns the aka field if avaliable and the path
* field if it's not
* @param dir an HShare directory descriptor
* @return the aka field or the path field, according to the logic described
*/
public String getAkaOrPathFromDir(Element dir){
return Statics.attributeValue(dir, "aka",dir.attributeValue("path"));
}
/**
* Given a HShare file descriptor, it returns the aka field if available and the
* filename if it's not
* @param file an HShare file descriptor
* @return the aka field or the filename field, according to the logic described
*/
public String getAkaOrNameFromFile(Element file){
return Statics.attributeValue(file, "aka",file.getStringValue());
}
/**
* Given an HShare descriptor, it returns the aka field or the path/filename
* @param el
* @return
*/
public String getAkaOrValue(Element el){
if(Statics.isADir(el))
return getAkaOrPathFromDir(el);
return getAkaOrNameFromFile(el);
}
/**
* Checks if a given file has been listed in a certain HShare directory descriptor
* @param file a FS file descriptor
* @param dir an HShare file descriptor
* @return true if the file has been listed
*/
@SuppressWarnings("unchecked")
private boolean isFileAlreadyListed(File file,Element dir){
Iterator<Element> its = dir.elements().iterator();
while(its.hasNext())
if(its.next().getStringValue().equals(file.getName()))
return true;
return false;
}
}