package com.limegroup.gnutella.library; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.limewire.core.settings.LibrarySettings; import org.limewire.listener.EventBroadcaster; import org.limewire.listener.SourcedEventMulticaster; import org.limewire.util.FileUtils; import org.limewire.util.MediaType; import org.limewire.util.StringUtils; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.assistedinject.Assisted; import com.limegroup.gnutella.URN; import com.limegroup.gnutella.library.SharedFileCollectionChangeEvent.Type; import com.limegroup.gnutella.tigertree.HashTreeCache; /** * A collection of FileDescs containing files that may be shared with one or more people. */ class SharedFileCollectionImpl extends AbstractFileCollection implements SharedFileCollection { private final int collectionId; private final Provider<LibraryFileData> data; private final HashTreeCache treeCache; private final EventBroadcaster<SharedFileCollectionChangeEvent> sharedBroadcaster; private final List<String> defaultFriendIds; private final boolean publicCollection; @Inject public SharedFileCollectionImpl(Provider<LibraryFileData> data, LibraryImpl managedList, SourcedEventMulticaster<FileViewChangeEvent, FileView> multicaster, EventBroadcaster<SharedFileCollectionChangeEvent> sharedCollectionBroadcaster, @Assisted int id, HashTreeCache treeCache, @Assisted boolean publicCollection, @Assisted String... defaultFriendIds) { super(managedList, multicaster); this.collectionId = id; this.data = data; this.treeCache = treeCache; this.sharedBroadcaster = sharedCollectionBroadcaster; this.publicCollection = publicCollection; if(defaultFriendIds.length == 0) { this.defaultFriendIds = Collections.emptyList(); } else { this.defaultFriendIds = Collections.unmodifiableList(Arrays.asList(defaultFriendIds)); } } @Override public int getId() { return collectionId; } public String getName() { return data.get().getNameForCollection(collectionId); } public void setName(String name) { if(data.get().setNameForCollection(collectionId, name)) { sharedBroadcaster.broadcast(new SharedFileCollectionChangeEvent(Type.NAME_CHANGED, this, name)); } } @Override public void addFriend(String id) { if(data.get().addFriendToCollection(collectionId, id)) { sharedBroadcaster.broadcast(new SharedFileCollectionChangeEvent(Type.FRIEND_ADDED, this, id)); } } @Override public boolean removeFriend(String id) { if(data.get().removeFriendFromCollection(collectionId, id)) { sharedBroadcaster.broadcast(new SharedFileCollectionChangeEvent(Type.FRIEND_REMOVED, this, id)); return true; } else { return false; } } @Override public List<String> getFriendList() { List<String> cached = data.get().getFriendsForCollection(collectionId); if(defaultFriendIds.isEmpty()) { return cached; } else if(cached.isEmpty()) { return defaultFriendIds; } else { List<String> friends = new ArrayList<String>(cached.size() + defaultFriendIds.size()); friends.addAll(defaultFriendIds); friends.addAll(cached); return friends; } } @Override public void setFriendList(List<String> ids) { List<String> oldIds = data.get().setFriendsForCollection(collectionId, ids); if(oldIds != null) { // if it changed, broadcast the change. sharedBroadcaster.broadcast(new SharedFileCollectionChangeEvent(Type.FRIEND_IDS_CHANGED, this, oldIds, ids)); } } @Override public String toString() { return StringUtils.toString(this) + ", name: " + getName(); } @Override protected boolean addFileDescImpl(FileDesc fileDesc) { if(super.addFileDescImpl(fileDesc)) { // if no root, calculate one and propagate it. if(fileDesc.getTTROOTUrn() == null) { // Schedule all additions for having a hash tree root. URN root = treeCache.getOrScheduleHashTreeRoot(fileDesc); if(root != null) { for(FileDesc fd : library.getFileDescsMatching(fileDesc.getSHA1Urn())) { fd.addUrn(root); } } } return true; } else { return false; } } @Override protected void initialize() { super.initialize(); addPendingManagedFiles(); } /** * This method initializes the friend file list. It adds the files * that are shared with the friend represented by this list. This * is necessary because friend file lists are populated/unpopulated when * needed, not upon startup. */ protected void addPendingManagedFiles() { // add files from the MASTER list which are for the current friend // normally we would not want to lock the master list while adding // items internally... but it's OK here because we're guaranteed // that nothing is listening to this list, since this will happen // immediately after construction. library.getReadLock().lock(); try { for (FileDesc fd : library) { if(isPending(fd.getFile(), fd)) { add(fd); } } } finally { library.getReadLock().unlock(); } } /** * Returns false if it's an {@link IncompleteFileDesc} or it's a store * file. */ @Override protected boolean isFileAddable(FileDesc fileDesc) { if (fileDesc instanceof IncompleteFileDesc) { return false; } else { return isFileAddable(fileDesc.getFile()); } } @Override protected boolean isPending(File file, FileDesc fd) { return data.get().isFileInCollection(file, collectionId); } @Override protected void saveChange(File file, boolean added) { data.get().setFileInCollection(file, collectionId, added); } @Override protected boolean clearImpl() { data.get().setFilesInCollection(this, collectionId, false); return super.clearImpl(); } @Override void dispose() { super.dispose(); data.get().removeCollection(collectionId); } @Override protected void fireAddEvent(FileDesc fileDesc) { super.fireAddEvent(fileDesc); } @Override protected void fireRemoveEvent(FileDesc fileDesc) { super.fireRemoveEvent(fileDesc); } @Override protected void fireChangeEvent(FileDesc oldFileDesc, FileDesc newFileDesc) { super.fireChangeEvent(oldFileDesc, newFileDesc); } @Override public boolean isFileAddable(File file) { if(!library.isFileAddable(file)) { return false; } if(isPublic()) { MediaType mediaType = MediaType.getMediaTypeForExtension(FileUtils.getFileExtension(file)); if(MediaType.getDocumentMediaType().equals(mediaType) && !LibrarySettings.ALLOW_DOCUMENT_GNUTELLA_SHARING.getValue()) { return false; } } return true; } @Override public boolean isPublic() { return publicCollection; } }