package org.limewire.core.impl.library; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.Collection; import org.limewire.collection.glazedlists.GlazedListsFactory; import org.limewire.core.api.Category; import org.limewire.core.api.library.LibraryManager; import org.limewire.core.api.library.LocalFileItem; import org.limewire.core.api.library.SharedFileList; import org.limewire.core.api.library.SharedFileListManager; import org.limewire.core.settings.LibrarySettings; import org.limewire.inject.EagerSingleton; import org.limewire.listener.EventListener; import org.limewire.listener.ListenerSupport; import org.limewire.listener.SwingSafePropertyChangeSupport; import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; import com.google.common.base.Predicate; import com.google.inject.Inject; import com.limegroup.gnutella.library.FileCollectionManager; import com.limegroup.gnutella.library.FileView; import com.limegroup.gnutella.library.FileViewChangeEvent; import com.limegroup.gnutella.library.SharedFileCollection; import com.limegroup.gnutella.library.SharedFileCollectionChangeEvent; import com.limegroup.gnutella.library.SharedFiles; @EagerSingleton class SharedFileListManagerImpl implements SharedFileListManager { // private static final Log LOG = LogFactory.getLog(ShareListManagerImpl.class); private final FileCollectionManager collectionManager; private final CoreLocalFileItemFactory coreLocalFileItemFactory; private final EventList<SharedFileList> sharedLists = GlazedListsFactory.threadSafeList(new BasicEventList<SharedFileList>()); private final EventList<SharedFileList> readOnlySharedLists = GlazedListsFactory.readOnlyList(sharedLists); private final FileView allSharedFilesView; private final PropertyChangeSupport changeSupport = new SwingSafePropertyChangeSupport(this); private volatile long listsCreatedV2; private volatile long listsShared; @Inject SharedFileListManagerImpl(FileCollectionManager collectionManager, CoreLocalFileItemFactory coreLocalFileItemFactory, LibraryManager libraryManager, @SharedFiles FileView allSharedFilesView) { this.collectionManager = collectionManager; this.coreLocalFileItemFactory = coreLocalFileItemFactory; this.allSharedFilesView = allSharedFilesView; } @Inject void register(ListenerSupport<SharedFileCollectionChangeEvent> support) { allSharedFilesView.addListener(new EventListener<FileViewChangeEvent>() { public void handleEvent(FileViewChangeEvent event) { switch(event.getType()) { case FILE_ADDED: case FILE_REMOVED: case FILES_CLEARED: changeSupport.firePropertyChange(SHARED_FILE_COUNT, null, allSharedFilesView.size()); break; } } }); for(SharedFileCollection collection : collectionManager.getSharedFileCollections()) { collectionAdded(collection); } support.addListener(new EventListener<SharedFileCollectionChangeEvent>() { @Override public void handleEvent(SharedFileCollectionChangeEvent event) { switch(event.getType()) { case COLLECTION_ADDED: collectionAdded(event.getSource()); break; case COLLECTION_REMOVED: collectionRemoved(event.getSource()); break; case FRIEND_ADDED: friendAddedToCollection(event.getSource(), event.getFriendId()); break; case FRIEND_IDS_CHANGED: friendsSetInCollection(event.getSource(), event.getNewFriendIds()); break; case FRIEND_REMOVED: friendRemoved(event.getSource(), event.getFriendId()); break; case NAME_CHANGED: nameChanged(event.getSource()); break; } } }); } // we technically don't have to change anything here, but we want to // make the list trigger an event to signify that something changed, // so we get the index of where it used to be & reset it. private void nameChanged(SharedFileCollection collection) { setListInPlace(collection); } /** * Sets the SharedFileListImpl that holds this collection in place, allowing * the model to trigger an update event. */ private void setListInPlace(SharedFileCollection collection) { sharedLists.getReadWriteLock().writeLock().lock(); try { for (int i = 0; i < sharedLists.size(); i++) { SharedFileListImpl impl = (SharedFileListImpl)sharedLists.get(i); if (impl.getCoreCollection() == collection) { sharedLists.set(i, impl); // reset it to trigger event. break; } } } finally { sharedLists.getReadWriteLock().writeLock().unlock(); } } private void friendRemoved(SharedFileCollection collection, String friendId) { SharedFileListImpl list = getListForCollection(collection); int oldSize = list.getFriendIds().size(); boolean removed = list.friendRemoved(friendId); // If we removed the last friend, reset the list to trigger an update event. if(oldSize == 1 && removed) { setListInPlace(collection); } } private void friendsSetInCollection(SharedFileCollection collection, Collection<String> newFriendIds) { SharedFileListImpl list = getListForCollection(collection); boolean wasEmpty = list.getFriendIds().isEmpty(); list.friendsSet(newFriendIds); boolean isEmpty = newFriendIds.isEmpty(); // if it changed from empty => not empty, or not empty => empty, trigger an update. if(wasEmpty != isEmpty) { if (!isEmpty) { listsShared++; } setListInPlace(collection); } } private void friendAddedToCollection(SharedFileCollection collection, String friendId) { SharedFileListImpl list = getListForCollection(collection); boolean wasEmpty = list.getFriendIds().isEmpty(); list.friendAdded(friendId); // if it used to be, trigger an update if(wasEmpty) { listsShared++; setListInPlace(collection); } } private void collectionAdded(SharedFileCollection collection) { SharedFileListImpl listImpl = new SharedFileListImpl(coreLocalFileItemFactory, collection); listImpl.friendsSet(collection.getFriendList()); sharedLists.add(listImpl); } private void collectionRemoved(SharedFileCollection collection) { sharedLists.remove(getListForCollection(collection)); } private SharedFileListImpl getListForCollection(SharedFileCollection collection) { sharedLists.getReadWriteLock().readLock().lock(); try { for (SharedFileList list : sharedLists) { SharedFileListImpl impl = (SharedFileListImpl) list; if (impl.getCoreCollection() == collection) { return impl; } } return null; } finally { sharedLists.getReadWriteLock().readLock().unlock(); } } @Override public void addPropertyChangeListener(PropertyChangeListener listener) { changeSupport.addPropertyChangeListener(listener); } @Override public void removePropertyChangeListener(PropertyChangeListener listener) { changeSupport.removePropertyChangeListener(listener); } @Override public int getSharedFileCount() { return allSharedFilesView.size(); } @Override public int createNewSharedFileList(String name) { listsCreatedV2++; return collectionManager.createNewCollection(name).getId(); } @Override public EventList<SharedFileList> getModel() { return readOnlySharedLists; } @Override public void deleteSharedFileList(SharedFileList fileList) { if(fileList instanceof SharedFileListImpl) { collectionManager.removeCollectionById(((SharedFileListImpl)fileList).getCoreCollection().getId()); } else { throw new IllegalStateException("invalid FileList: " + fileList); } } @Override public void removeDocumentsFromPublicLists() { LibrarySettings.ALLOW_DOCUMENT_GNUTELLA_SHARING.setValue(false); EventList<SharedFileList> shareLists = getModel(); shareLists.getReadWriteLock().readLock().lock(); try { for (SharedFileList sharedFileList : shareLists) { if (sharedFileList.isPublic()) { sharedFileList.removeFiles(new Predicate<LocalFileItem>() { @Override public boolean apply(LocalFileItem localFileItem) { return localFileItem.getCategory() == Category.DOCUMENT; } }); } } } finally { shareLists.getReadWriteLock().readLock().unlock(); } } }