/*
* Copyright (C) 2009-2011 Geometer Plus <contact@geometerplus.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.fbreader.library;
import java.util.*;
import org.geometerplus.zlibrary.core.util.ZLMiscUtil;
import org.geometerplus.zlibrary.core.filesystem.*;
public final class FileInfoSet {
private static final class Pair {
private final String myName;
private final FileInfo myParent;
Pair(String name, FileInfo parent) {
myName = name;
myParent = parent;
}
@Override
public int hashCode() {
return (myParent == null) ? myName.hashCode() : myParent.hashCode() + myName.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Pair)) {
return false;
}
Pair p = (Pair)o;
return (myName.equals(p.myName)) && ZLMiscUtil.equals(myParent, p.myParent);
}
}
private final HashMap<ZLFile,FileInfo> myInfosByFile = new HashMap<ZLFile,FileInfo>();
private final HashMap<FileInfo,ZLFile> myFilesByInfo = new HashMap<FileInfo,ZLFile>();
private final HashMap<Pair,FileInfo> myInfosByPair = new HashMap<Pair,FileInfo>();
private final HashMap<Long,FileInfo> myInfosById = new HashMap<Long,FileInfo>();
private final LinkedHashSet<FileInfo> myInfosToSave = new LinkedHashSet<FileInfo>();
private final LinkedHashSet<FileInfo> myInfosToRemove = new LinkedHashSet<FileInfo>();
public FileInfoSet() {
load(BooksDatabase.Instance().loadFileInfos());
}
public FileInfoSet(ZLFile file) {
load(BooksDatabase.Instance().loadFileInfos(file));
}
FileInfoSet(long fileId) {
load(BooksDatabase.Instance().loadFileInfos(fileId));
}
private void load(Collection<FileInfo> infos) {
for (FileInfo info : infos) {
myInfosByPair.put(new Pair(info.Name, info.Parent), info);
myInfosById.put(info.Id, info);
}
}
public void save() {
final BooksDatabase database = BooksDatabase.Instance();
database.executeAsATransaction(new Runnable() {
public void run() {
for (FileInfo info : myInfosToRemove) {
database.removeFileInfo(info.Id);
myInfosByPair.remove(new Pair(info.Name, info.Parent));
}
myInfosToRemove.clear();
for (FileInfo info : myInfosToSave) {
database.saveFileInfo(info);
}
myInfosToSave.clear();
}
});
}
public boolean check(ZLPhysicalFile file, boolean processChildren) {
if (file == null) {
return true;
}
final long fileSize = file.size();
FileInfo info = get(file);
if (info.FileSize == fileSize) {
return true;
} else {
info.FileSize = fileSize;
if (processChildren) {
removeChildren(info);
myInfosToSave.add(info);
addChildren(file);
} else {
myInfosToSave.add(info);
}
return false;
}
}
public List<ZLFile> archiveEntries(ZLFile file) {
final FileInfo info = get(file);
if (!info.hasChildren()) {
return Collections.emptyList();
}
final LinkedList<ZLFile> entries = new LinkedList<ZLFile>();
for (FileInfo child : info.subTrees()) {
if (!myInfosToRemove.contains(child)) {
entries.add(ZLArchiveEntryFile.createArchiveEntryFile(file, child.Name));
}
}
return entries;
}
private FileInfo get(String name, FileInfo parent) {
final Pair pair = new Pair(name, parent);
FileInfo info = myInfosByPair.get(pair);
if (info == null) {
info = new FileInfo(name, parent);
myInfosByPair.put(pair, info);
myInfosToSave.add(info);
}
return info;
}
private FileInfo get(ZLFile file) {
if (file == null) {
return null;
}
FileInfo info = myInfosByFile.get(file);
if (info == null) {
info = get(file.getLongName(), get(file.getParent()));
myInfosByFile.put(file, info);
}
return info;
}
public long getId(ZLFile file) {
final FileInfo info = get(file);
if (info == null) {
return -1;
}
if (info.Id == -1) {
save();
}
return info.Id;
}
private ZLFile getFile(FileInfo info) {
if (info == null) {
return null;
}
ZLFile file = myFilesByInfo.get(info);
if (file == null) {
file = ZLFile.createFile(getFile(info.Parent), info.Name);
myFilesByInfo.put(info, file);
}
return file;
}
public ZLFile getFile(long id) {
return getFile(myInfosById.get(id));
}
private void removeChildren(FileInfo info) {
for (FileInfo child : info.subTrees()) {
if (myInfosToSave.contains(child)) {
myInfosToSave.remove(child);
} else {
myInfosToRemove.add(child);
}
removeChildren(child);
}
}
private void addChildren(ZLFile file) {
for (ZLFile child : file.children()) {
final FileInfo info = get(child);
if (myInfosToRemove.contains(info)) {
myInfosToRemove.remove(info);
} else {
myInfosToSave.add(info);
}
addChildren(child);
}
}
}