/*
* 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.ui.model;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.swing.ListModel;
import com.jgoodies.binding.list.ArrayListModel;
import com.jgoodies.binding.value.ValueModel;
import de.dal33t.powerfolder.Controller;
import de.dal33t.powerfolder.Member;
import de.dal33t.powerfolder.ui.PFUIComponent;
import de.dal33t.powerfolder.PreferencesEntry;
import de.dal33t.powerfolder.clientserver.ServerClientEvent;
import de.dal33t.powerfolder.clientserver.ServerClientListener;
import de.dal33t.powerfolder.event.NodeManagerAdapter;
import de.dal33t.powerfolder.event.NodeManagerEvent;
import de.dal33t.powerfolder.event.NodeManagerModelListener;
import de.dal33t.powerfolder.net.NodeManager;
import de.dal33t.powerfolder.security.SecurityManagerEvent;
import de.dal33t.powerfolder.security.SecurityManagerListener;
import de.dal33t.powerfolder.util.compare.MemberComparator;
/**
* Model for the node manager. Create filtered list of nodes based on friend and
* lan values.
*
* @author <a href="mailto:totmacher@powerfolder.com">Christian Sprajc</a>
* @author <a href="mailto:schaatser@powerfolder.com">Jan van Oosterom</a>
* @author <a href="mailto:harry@powerfolder.com">Harry Glasgow</a>
* @version $Revision: 1.5 $
*/
public class NodeManagerModel extends PFUIComponent {
public enum Type {
MY_COMPUTERS_INDEX, FRIENDS_INDEX, CONNECTED_LAN;
}
private ValueModel showOfflineModel;
private ArrayListModel<Member> friendsListModel;
private final Set<Member> nodes;
private final NodeManager nodeManager;
private final Set<NodeManagerModelListener> listeners;
/**
* Constructor
*
* @param controller
*/
public NodeManagerModel(Controller controller) {
super(controller);
nodes = new CopyOnWriteArraySet<Member>();
nodeManager = getController().getNodeManager();
listeners = new CopyOnWriteArraySet<NodeManagerModelListener>();
initalize();
}
/**
* Add a node manager model listener.
*
* @param l
*/
public void addNodeManagerModelListener(NodeManagerModelListener l) {
listeners.add(l);
}
/**
* Remove a node manager model listener.
*
* @param l
*/
public void removeNodeManagerModelListener(NodeManagerModelListener l) {
listeners.remove(l);
}
/**
* Initalize all nessesary ui models
*/
private synchronized void initalize() {
PropertyChangeListener propertyChangeListener = new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent evt) {
rebuildSet();
}
};
friendsListModel = new ArrayListModel<Member>();
showOfflineModel = PreferencesEntry.NODE_MANAGER_MODEL_SHOW_OFFLINE
.getModel(getController());
showOfflineModel.addValueChangeListener(propertyChangeListener);
rebuildSet();
// Register listener on node manager
nodeManager.addNodeManagerListener(new MyNodeManagerListener());
getController().getSecurityManager().addListener(
new MySecurityManagerListener());
getController().getOSClient().addListener(new MyServerClientListener());
}
/**
* Rebuild the whole node list because of big change.
*/
private void rebuildSet() {
Collection<Member> nodeCollection = nodeManager.getNodesAsCollection();
Set<Member> newSet = new TreeSet<Member>(MemberComparator.NICK);
for (Member node : nodeCollection) {
if (required(node)) {
newSet.add(node);
}
}
nodes.clear();
nodes.addAll(newSet);
for (NodeManagerModelListener listener : listeners) {
listener.changed();
}
Member[] friends = getController().getNodeManager().getFriends();
friendsListModel.clear();
friendsListModel.addAll(Arrays.asList(friends));
Collections.sort(friendsListModel, MemberComparator.IN_GUI);
}
/**
* Answers if the node is required based on value models. This only returns
* nodes that are my computer, friend or connected lan. Also filters online
* based on showOfflineModel
*
* @param node
* @return
*/
private boolean required(Member node) {
boolean connected = node.isCompletelyConnected();
if (node.isMySelf()) {
// Never add self
return false;
}
// Only care about 1) my computers, 2) friends, and 3) connected lan
if (node.isMyComputer() || node.isFriend() || node.isOnLAN()
&& connected)
{
// Wanted, now check online.
boolean showOffline = (Boolean) showOfflineModel.getValue();
boolean online = connected || node.isConnectedToNetwork();
return showOffline || online;
} else {
return false;
}
}
/**
* Returns a Map of Members. Map 0 is My Computers Map 1 is Friends Map 2 is
* Connected LAN
*
* @return
*/
public Map<Type, Set<Member>> getNodesMap() {
// Split nodes into three groups:
// 1) My Computers,
// 2) Friends and
// 3) Connected LAN
// Use maps to sort by name.
Set<Member> myComputers = new HashSet<Member>();
Set<Member> friends = new HashSet<Member>();
Set<Member> connectedLans = new HashSet<Member>();
// Make a copy, so nodes are not affected.
Set<Member> copy = new HashSet<Member>();
copy.addAll(nodes);
for (Iterator<Member> iterator = copy.iterator(); iterator.hasNext();) {
Member member = iterator.next();
// My computers should get automatically friends by
// ServerClient.updateFriendsList(..)
if (member.isFriend() && member.isMyComputer()) {
myComputers.add(member);
iterator.remove();
}
}
for (Iterator<Member> iterator = copy.iterator(); iterator.hasNext();) {
Member member = iterator.next();
if (member.isOnLAN() && member.isCompletelyConnected()) {
connectedLans.add(member);
iterator.remove();
}
}
for (Member member : copy) {
if (member.isFriend()) {
friends.add(member);
}
}
Map<Type, Set<Member>> resultsMap = new TreeMap<Type, Set<Member>>();
resultsMap.put(Type.MY_COMPUTERS_INDEX, myComputers);
resultsMap.put(Type.FRIENDS_INDEX, friends);
resultsMap.put(Type.CONNECTED_LAN, connectedLans);
return resultsMap;
}
/**
* Returns a count of the nodes in the three groups.
*
* @return
*/
public int getSize() {
int count = 0;
Map<Type, Set<Member>> map = getNodesMap();
for (Set<Member> members : map.values()) {
count += members.size();
}
return count;
}
/**
* @return if offline friends should be shown.
*/
public ValueModel getShowOfflineModel() {
return showOfflineModel;
}
/**
* @return a listmodel that contains the friendslist.
*/
public ListModel getFriendsListModel() {
return friendsListModel;
}
/**
* Update method responding to changes of nodes. Fire changed if added,
* removed or in list.
*
* @param node
*/
private void updateNode(Member node) {
boolean changed = false;
boolean wanted = required(node);
if (wanted) {
if (!nodes.contains(node)) {
nodes.add(node);
changed = true;
}
} else {
if (nodes.contains(node)) {
nodes.remove(node);
changed = true;
}
}
if (nodes.contains(node)) {
changed = true;
}
if (changed) {
for (NodeManagerModelListener listener : listeners) {
listener.changed();
}
}
}
/**
* Listens for changes in the node manager
*/
private class MyNodeManagerListener extends NodeManagerAdapter {
public void friendAdded(NodeManagerEvent e) {
if (!friendsListModel.contains(e.getNode())) {
friendsListModel.add(e.getNode());
Collections.sort(friendsListModel, MemberComparator.IN_GUI);
}
updateNode(e.getNode());
}
public void friendRemoved(NodeManagerEvent e) {
friendsListModel.remove(e.getNode());
Collections.sort(friendsListModel, MemberComparator.IN_GUI);
updateNode(e.getNode());
}
public void nodeAdded(NodeManagerEvent e) {
updateNode(e.getNode());
}
public void nodeConnected(NodeManagerEvent e) {
updateNode(e.getNode());
}
public void nodeDisconnected(NodeManagerEvent e) {
updateNode(e.getNode());
}
public void nodeOffline(NodeManagerEvent e) {
updateNode(e.getNode());
}
public void nodeOnline(NodeManagerEvent e) {
updateNode(e.getNode());
}
public void nodeRemoved(NodeManagerEvent e) {
updateNode(e.getNode());
}
public void settingsChanged(NodeManagerEvent e) {
updateNode(e.getNode());
}
public boolean fireInEventDispatchThread() {
return true;
}
}
private class MySecurityManagerListener implements SecurityManagerListener {
public void nodeAccountStateChanged(SecurityManagerEvent event) {
updateNode(event.getNode());
}
public boolean fireInEventDispatchThread() {
return true;
}
}
private class MyServerClientListener implements ServerClientListener {
public void accountUpdated(ServerClientEvent event) {
updateNode(getController().getMySelf());
}
public void login(ServerClientEvent event) {
updateNode(getController().getMySelf());
}
public void nodeServerStatusChanged(ServerClientEvent event) {
updateNode(event.getServerNode());
}
public void serverConnected(ServerClientEvent event) {
}
public void serverDisconnected(ServerClientEvent event) {
}
public boolean fireInEventDispatchThread() {
return true;
}
}
}