/*
* Copyright 2004 - 2008 Christian Sprajc. All rights reserved.
*
* This file is part of PowerFolder.
*
* PowerFolder 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.
*
* PowerFolder 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 PowerFolder. If not, see <http://www.gnu.org/licenses/>.
*
* $Id$
*/
package de.dal33t.powerfolder.message;
import java.io.Externalizable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import de.dal33t.powerfolder.Constants;
import de.dal33t.powerfolder.disk.DiskItemFilter;
import de.dal33t.powerfolder.disk.Folder;
import de.dal33t.powerfolder.light.DirectoryInfo;
import de.dal33t.powerfolder.light.FileInfo;
import de.dal33t.powerfolder.light.FolderInfo;
import de.dal33t.powerfolder.util.Reject;
/**
* Files of a folder.
* <p>
* TODO Improve splitting. Should act upon a List<FileInfo> instead of array
*
* @see de.dal33t.powerfolder.message.FolderFilesChanged
* @author <a href="mailto:totmacher@powerfolder.com">Christian Sprajc</a>
* @version $Revision: 1.5 $
*/
public class FileList extends FolderRelatedMessage {
private static final Logger log = Logger
.getLogger(FileList.class.getName());
private static final long serialVersionUID = 100L;
public FileInfo[] files;
/**
* The number of following delta filelist to expect.
*
* @see FolderFilesChanged
*/
public int nFollowingDeltas;
protected FileList() {
// Serialization
}
protected FileList(FolderInfo folderInfo, FileInfo[] files,
int nDetlas2Follow)
{
Reject.ifNull(folderInfo, "FolderInfo is null");
Reject.ifNull(files, "Files is null");
Reject.ifTrue(nDetlas2Follow < 0,
"Invalid number for following detla messages");
this.files = files;
this.folder = folderInfo;
this.nFollowingDeltas = nDetlas2Follow;
}
protected FileList(FolderInfo folderInfo) {
Reject.ifNull(folderInfo, "FolderInfo is null");
this.files = null;
this.folder = folderInfo;
this.nFollowingDeltas = 0;
}
/**
* Just to inform that we won't or can't send any information about the
* files.
*
* @param foInfo
* @param useExt
* if use {@link Externalizable}s
* @return a list that contains null information about the files in a
* folder.
*/
public static FileList createEmpty(FolderInfo foInfo, boolean useExt) {
if (useExt) {
return new FileListExt(foInfo);
}
return new FileList(foInfo);
}
/**
* Creates the message for the filelist. Filelist gets splitted into smaller
* ones if required.
*
* @param folder
* @param useExt
* if use {@link Externalizable}s
* @return the splitted filelist messages.
*/
public static Message[] create(Folder folder, boolean useExt) {
return createFileListMessages(folder.getInfo(), folder.getKnownFiles(),
folder.getKnownDirectories(), folder.getDiskItemFilter(), useExt);
}
/**
* Splits the filelist into smaller ones. Splits into one
* <code>FileList</code> and (if required) multiple
* <code>FolderFilesChanged</code> messages
*
* @param foInfo
* the folder for the message
* @param files
* the fileinfos to include.
* @param diskItemFilter
* the item filter to appy
* @return the splitted list
*/
public static Message[] create4Test(FolderInfo foInfo,
Collection<FileInfo> files, DiskItemFilter diskItemFilter)
{
Collection<DirectoryInfo> dirInfos = Collections.emptyList();
return createFileListMessages(foInfo, files, dirInfos, diskItemFilter,
true);
}
/**
* Splits the filelist into smaller ones. Always splits into one
* <code>FileList</code> and (if required) multiple
* <code>FolderFilesChanged</code> messages
*
* @param foInfo
* the folder for the message
* @param files
* the fileinfos to include.
* @param blacklist
* the blacklist to apply * @param onlyChanges if the messages
* should contain only {@link FolderFilesChanged} or contain a
* {@link FileList} as first message.
* @return the splitted list
*/
private static Message[] createFileListMessages(FolderInfo foInfo,
Collection<FileInfo> files, Collection<DirectoryInfo> dirs,
DiskItemFilter diskItemFilter, boolean useExt)
{
Reject.ifNull(foInfo, "Folder info is null");
Reject.ifNull(files, "Files is null");
Reject.ifNull(diskItemFilter, "DiskItemFilter is null");
Reject.ifTrue(Constants.FILE_LIST_MAX_FILES_PER_MESSAGE <= 0,
"Unable to split filelist. nFilesPerMessage: "
+ Constants.FILE_LIST_MAX_FILES_PER_MESSAGE);
if (files.isEmpty() && dirs.isEmpty()) {
if (useExt) {
return new Message[]{new FileListExt(foInfo, new FileInfo[0], 0)};
} else {
return new Message[]{new FileList(foInfo, new FileInfo[0], 0)};
}
}
List<Message> messages = new ArrayList<Message>(files.size()
/ Constants.FILE_LIST_MAX_FILES_PER_MESSAGE);
int nDeltas = 0;
boolean firstMessage = true;
int curMsgIndex = 0;
FileInfo[] messageFiles = new FileInfo[Constants.FILE_LIST_MAX_FILES_PER_MESSAGE];
for (FileInfo fileInfo : files) {
if (diskItemFilter.isExcluded(fileInfo)) {
continue;
}
messageFiles[curMsgIndex] = fileInfo;
curMsgIndex++;
if (curMsgIndex >= Constants.FILE_LIST_MAX_FILES_PER_MESSAGE) {
if (firstMessage) {
if (useExt) {
messages.add(new FileListExt(foInfo, messageFiles, 0));
} else {
messages.add(new FileList(foInfo, messageFiles, 0));
}
firstMessage = false;
} else {
nDeltas++;
if (useExt) {
messages.add(new FolderFilesChangedExt(foInfo,
messageFiles));
} else {
messages.add(new FolderFilesChanged(foInfo,
messageFiles));
}
}
messageFiles = new FileInfo[Constants.FILE_LIST_MAX_FILES_PER_MESSAGE];
curMsgIndex = 0;
}
}
for (DirectoryInfo dirInfo : dirs) {
if (diskItemFilter.isExcluded(dirInfo)) {
continue;
}
messageFiles[curMsgIndex] = dirInfo;
curMsgIndex++;
if (curMsgIndex >= Constants.FILE_LIST_MAX_FILES_PER_MESSAGE) {
if (firstMessage) {
if (useExt) {
messages.add(new FileListExt(foInfo, messageFiles, 0));
} else {
messages.add(new FileList(foInfo, messageFiles, 0));
}
firstMessage = false;
} else {
nDeltas++;
if (useExt) {
messages.add(new FolderFilesChangedExt(foInfo,
messageFiles));
} else {
messages.add(new FolderFilesChanged(foInfo,
messageFiles));
}
}
messageFiles = new FileInfo[Constants.FILE_LIST_MAX_FILES_PER_MESSAGE];
curMsgIndex = 0;
}
}
if (firstMessage && curMsgIndex == 0) {
// Only ignored files
if (useExt) {
return new Message[]{new FileListExt(foInfo, new FileInfo[0], 0)};
} else {
return new Message[]{new FileList(foInfo, new FileInfo[0], 0)};
}
}
if (curMsgIndex != 0 && curMsgIndex < messageFiles.length) {
// Last message
FileInfo[] lastFiles = new FileInfo[curMsgIndex];
System.arraycopy(messageFiles, 0, lastFiles, 0, lastFiles.length);
if (firstMessage) {
if (useExt) {
messages.add(new FileListExt(foInfo, lastFiles, 0));
} else {
messages.add(new FileList(foInfo, lastFiles, 0));
}
firstMessage = false;
} else {
nDeltas++;
if (useExt) {
messages.add(new FolderFilesChangedExt(foInfo, lastFiles));
} else {
messages.add(new FolderFilesChanged(foInfo, lastFiles));
}
}
}
// Set the actual number of deltas
if (!messages.isEmpty() && messages.get(0) instanceof FileList) {
((FileList) messages.get(0)).nFollowingDeltas = nDeltas;
}
if (log.isLoggable(Level.FINER)) {
log.finer("Splitted filelist into " + messages.size()
+ ", deltas: " + nDeltas + ", folder: " + foInfo + ", files: "
+ files.size() + ", dirs: " + dirs.size() + "\nSplitted msgs: "
+ messages);
}
return messages.toArray(new Message[messages.size()]);
}
/**
* A filelist that does contains any information of the remote folder.
*
* @return if this filelist contains null / nothing / nada.
*/
public boolean isNull() {
return files == null;
}
public String toString() {
return "FileList of "
+ folder
+ ": "
+ (files != null
? files.length + " file(s)"
: "No information about files.");
}
}