Package ymsg.support

Source Code of ymsg.support.SwingModelFactory$AbstractUserModel

package ymsg.support;

import java.lang.ref.*;
import java.util.*;
import javax.swing.ListModel;
import javax.swing.event.*;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import ymsg.network.*;
import ymsg.network.event.*;

// *********************************************************************
// This is a handy class for creating self-updating models. for use with
// Swing's MVC scheme.
//
// This class is rather clever.  It will register itself with the session
// provided, and enacts any events from said session on the model objects
// it creates automatically.  Eg, having created a ListModel for a chat
// room, this class will automatically update the ListModel's status, based
// upon messages received from Yahoo (or rather, the session events they
// generate).
//
// Even more clever is that this class uses weak references to automatically
// drop any models no longer referenced 'outside' the factory - meaning that
// the API user does not explicitly have to tell the factory when a model
// is being discarded - they can just null all references like normal, in the
// knowledge that the factory will detect this and remove its own references!
//
// Lifecycle:
// 1)  The user creates a new model.  The model is remembered by the
//     factory in a hash.  The key is the Lobby/Conference/whatever object
//    containing the member list being modelled.  The value is a weak
//    reference to the model object itself.  The weak reference is also
//    queued for notification after it is wiped.
// 2)  An Session event occures, and the factory looks the model up in the
//    hash, using the Lobby/Conference/etc object from the event as the
//    key.  If present, the model will be updated.
// 3)  When the user discards the model object, the garbage collector
//    realises that only the weak reference in the factory's hash is
//    holding the model - so it releases the model and adds the weak
//    reference to the queue.
// 4)  Getting the weak reference from the queue, the factory now knows
//    that the model object has been discarded 'outside', and removes
//    the key and weak reference from the hash, thus successfully
//    cleaning up all outstanding objects.
//
// AbstractUserModel is an inner class, which provides useful utility
// code for other models, namely storing and firing listener events.
// The FireEvent inner class spawns a new thread and works through the
// vector of listeners, involking an abstract method on the subclass to
// call the correct listener method, depending upon the mode;
//
// Note: you should not create duplicate models for the same data, as
// subsequent calls will create models which superceed previous models.
// *********************************************************************
public class SwingModelFactory extends SessionAdapter
private Session session;          // Session to monitor
  private Hashtable models;          // Current models
  private ReferenceQueue queue;        // Weak ref queue
  private QueueHandler queueHandler;      // Thread to remove weak refs
  private ThreadGroup factoryThreads;      // Thread group

  private static UserComparator userComparator; // Compare user objects
  private static GroupComparator groupComparator; // Compare group objects
  private static DummyListModel dummyListModel; // Empty list
  private static String ROOT = "Groups";    // Represents tree root node

  // -----------------------------------------------------------------
  // CONSTRUCTORS
  // -----------------------------------------------------------------
  public SwingModelFactory(Session ss)
  {  session=ss;  session.addSessionListener(this);
    models = new Hashtable();
    factoryThreads = new ThreadGroup("Swing Model Threads");

    // -----This process takes defunct weak references and removed them
    // -----from the model hashtable
    queue = new ReferenceQueue();
    queueHandler = new QueueHandler();
  }

  static
  userComparator = new UserComparator();
    groupComparator = new GroupComparator();
    dummyListModel = new DummyListModel();
  }

  // -----------------------------------------------------------------
  // DESTRUCTOR
  // -----------------------------------------------------------------
  public void finalize() throws Throwable
  try
    session.removeSessionListener(this)// Remove session listener
      queueHandler.quit();          // Stop weak ref queue
    }
    catch(Exception e) {}
    finally { super.finalize(); }
  }

  // -----------------------------------------------------------------
  // Session listener methods.  These feed the update of our models.
  // -----------------------------------------------------------------
  public void chatLogonReceived(SessionChatEvent ev)
  {  UserListModel ulm = (UserListModel)getReference(ev.getLobby());
    YahooChatUser[] u = ev.getChatUsers();
    if(ulm!=null)
      for(int i=0;i<u.length;i++ulm.addUser(u[i]);
  }
  public void chatLogoffReceived(SessionChatEvent ev)
  {  UserListModel ulm = (UserListModel)getReference(ev.getLobby());
    YahooChatUser u = ev.getChatUser();
    if(ulm!=nullulm.removeUser(u);
  }
  public void chatUserUpdateReceived(SessionChatEvent ev)
  {  UserListModel ulm = (UserListModel)getReference(ev.getLobby());
    YahooChatUser u = ev.getChatUser();
    if(ulm!=nullulm.updateUser(u);
  }

  public void conferenceInviteReceived(SessionConferenceEvent ev)
  {  UserListModel ulm = (UserListModel)getReference(ev.getRoom());
    YahooUser[] u = ev.getUsers();
    if(ulm!=null)
      for(int i=0;i<u.length;i++ulm.addUser(u[i]);
  }
  public void conferenceLogonReceived(SessionConferenceEvent ev)
  {  UserListModel ulm = (UserListModel)getReference(ev.getRoom());
    YahooUser u = ev.getUser();
    if(ulm!=nullulm.removeUser(u);
  }
  public void conferenceLogoffReceived(SessionConferenceEvent ev)
  {  UserListModel ulm = (UserListModel)getReference(ev.getRoom());
    YahooUser[] u = ev.getUsers();
    if(ulm!=nullulm.removeUser(u);
  }

  public void listReceived(SessionEvent ev)
  {  UserTreeModel utm = (UserTreeModel)getReference(ROOT);
    if(utm!=nullutm.updateAll();
  }
  public void friendsUpdateReceived(SessionFriendEvent ev)
  {  UserTreeModel utm = (UserTreeModel)getReference(ROOT);
    if(utm!=nullutm.updateUser(ev.getFriend(),ev.getGroup());
  }
  public void friendAddedReceived(SessionFriendEvent ev)
  {  UserTreeModel utm = (UserTreeModel)getReference(ROOT);
    if(utm!=nullutm.addUser(ev.getFriend(),ev.getGroup());
  }
  public void friendRemovedReceived(SessionFriendEvent ev)
  {  UserTreeModel utm = (UserTreeModel)getReference(ROOT);
    if(utm!=nullutm.removeUser(ev.getFriend(),ev.getGroup());
  }


  // -----------------------------------------------------------------
  // Util list methods
  // -----------------------------------------------------------------
  private Object getReference(Object k)
  {  Reference ref=(Reference)models.get(k);
    if(ref!=nullreturn ref.get();
      else  return null;
  }

  /*private YahooGroup getGroup(String n)
  {  YahooGroup[] yg = session.getGroups();
    for(int i=0;i<yg.length;i++)
      if(yg[i].getName().equals(n))  return  yg[i];
    return null;
  }*/

  // -----------------------------------------------------------------
  // The hash of current models is needed so that when events arrive
  // from the Session we are listening to, we can dispatch them to
  // all relevant models.
  //
  // Weak references are used because this list of models should not
  // prevent model objects from being garbage collected once they have
  // been discarded outside this factory.  This removes the need for
  // API users to formally tell the factory when they have finished
  // with a model, so it can be removed from the factory's hash, and
  // made eligable to complete GC'ing.
  // -----------------------------------------------------------------
  private void addModel(Object key,Object model)
  {  models.put(key,new WeakReference(model,queue));
  }

  // -----------------------------------------------------------------
  // These methods are used to create the various model classes
  // -----------------------------------------------------------------
  public ListModel createListModel(YahooChatLobby ycl,boolean sort)
  {  ListModel lm = new UserListModel(ycl.getMembers(),sort);
    addModel(ycl,lm)return lm;
  }

  public ListModel createListModel(YahooConference yc,boolean sort)
  {  ListModel lm = new UserListModel(yc.getMembers(),sort);
    addModel(yc,lm)return lm;
  }

  public TreeModel createTreeModel(boolean sort)
  {  YahooGroup[] yg = session.getGroups();
    TreeModel tm = new UserTreeModel(yg,sort);
    addModel(ROOT,tm)return tm;
  }

  // -----------------------------------------------------------------
  // Static method to get an empty list model - handy for blanking list
  // contents.
  // -----------------------------------------------------------------
  public static ListModel getEmptyListModel()
  {  return dummyListModel;
  }

  // *****************************************************************
  // Used as a base class for the other models.  Provides listener
  // utility functionality, common to all models.
  // *****************************************************************
  abstract class AbstractUserModel
  Vector listeners;
    boolean sort;

    AbstractUserModel(boolean st) { listeners = new Vector();  sort=st; }
    void addListener(Object o) { if(!listeners.contains(o))  listeners.addElement(o); }
    void removeListener(Object o) { listeners.removeElement(o); }

    abstract void fireEvent(Object listener,EventObject ev,int mode);

    // *************************************************************
    // Threaded event dispatch - defers to concrete method in subclass
    // *************************************************************
    class FireEvent extends Thread
    EventObject ev;
      int mode=-1;
      FireEvent(EventObject e,int m)
      {  super(factoryThreads,"Event Fired");
        ev=e;  mode=m;  this.start();
      }
      public void run()
      {  for(int i=0;i<listeners.size();i++)
          fireEvent(listeners.elementAt(i),ev,mode);
      }
    }
  }


  // *****************************************************************
  // Class for ListModel type models
  // *****************************************************************
  class UserListModel extends AbstractUserModel implements ListModel
  private Vector members;          // Items in out list

    // -------------------------------------------------------------
    // CONSTRUCTOR
    // -------------------------------------------------------------
    UserListModel(Vector v,boolean st)
    {  super(st);  members=v;
      if(sortCollections.sort(members,userComparator);
    }

    // -------------------------------------------------------------
    // Add/remove/update - called by the session event methods
    // -------------------------------------------------------------
    void addUser(Object o)
    {  synchronized(members)
      {  int p=-1;
        members.addElement(o);
        if(sort)
        {  Collections.sort(members,userComparator);
          p=members.indexOf(o);
        }
        else
        p=members.size()-1;
        }
        ListDataEvent lde = new ListDataEvent(this,ListDataEvent.INTERVAL_ADDED,p,p);
        new FireEvent(lde,1);
      }
    }
    void removeUser(Object o)
    {  synchronized(members)
      {  int p=members.indexOf(o);
        if(p>-1)
        {  members.removeElementAt(p);
          ListDataEvent lde = new ListDataEvent(this,ListDataEvent.INTERVAL_REMOVED,p,p);
          new FireEvent(lde,2);
        }
      }
    }
    void updateUser(Object o)
    {  synchronized(members)
      {  int p=members.indexOf(o);
        if(p>-1)
        {  ListDataEvent lde = new ListDataEvent(this,ListDataEvent.CONTENTS_CHANGED,p,p);
          new FireEvent(lde,3);
        }
      }
    }

    // -------------------------------------------------------------
    // This is called by the FireEvent inner class of the super class
    // -------------------------------------------------------------
    void fireEvent(Object listener,EventObject ev,int mode)
    {  ListDataListener l = (ListDataListener)listener;
      ListDataEvent lde = (ListDataEvent)ev;
      switch(mode)
      {  case 1 :  l.intervalAdded(lde)break;
        case 2 :  l.intervalRemoved(lde)break;
        case 3 :  l.contentsChanged(lde)break;
        default return;
      }
    }

    // -------------------------------------------------------------
    // List model methods
    // -------------------------------------------------------------
    public void addListDataListener(ListDataListener l) { addListener(l); }
    public void removeListDataListener(ListDataListener l) { removeListener(l); }
    public int getSize() { return members.size(); }
    public Object getElementAt(int idx) { return members.elementAt(idx); }
  }

  // *****************************************************************
  // Class for TreeModel type models.
  // *****************************************************************
  class UserTreeModel extends AbstractUserModel implements TreeModel
  Hashtable groups;
    Vector groupOrder;

    // -------------------------------------------------------------
    // CONSTRUCTOR
    // -------------------------------------------------------------
    UserTreeModel(YahooGroup[] g,boolean st)
    {  super(st);  groups = new Hashtable();  groupOrder = new Vector();

      // -----Copy the groups, so we can manipulate/sort them
      for(int i=0;i<g.length;i++)
      {  Vector v = g[i].getMembers();
        if(sortCollections.sort(v,userComparator);
        groups.put(g[i].getName(),v);  groupOrder.addElement(g[i].getName());
      }
      if(sortCollections.sort(groupOrder,groupComparator);
    }

    // -------------------------------------------------------------
    // Add/remove/update - called by the session event methods
    // -------------------------------------------------------------
    void addUser(YahooUser yu,String yg)
    {  Object[] path,childObj;
      int[] childIdx;

      Vector v = (Vector)groups.get(yg);
      if(v==null)
      {  // -----Send event to add new group (with new user)
        v = new Vector();  v.addElement(yu);
        groups.put(yg,v);  groupOrder.addElement(yg);
        Collections.sort(groupOrder,groupComparator);
        path = new Object[1];  path[0]=ROOT;
        childIdx = new int[1];  childIdx[0]=groupOrder.indexOf(yg);
        childObj = new Object[1];  childObj[0]=yg;
      }
      else
      // -----Send event to add new user in group
        v.addElement(yu);
        Collections.sort(v,userComparator);
        path = new Object[2];  path[0]=ROOT;  path[1]=yg;
        childIdx = new int[1];  childIdx[0]=v.indexOf(yu);
        childObj = new Object[1];  childObj[0]=yg;
      }
      TreeModelEvent tme = new TreeModelEvent(this,path,childIdx,childObj);
      new FireEvent(tme,1);
    }
    void removeUser(YahooUser yu,String yg)
    {  Object[] path,childObj;
      int[] childIdx;

      Vector v = (Vector)groups.get(yg);
      if(v.size()<=1)
      {  // -----Send event to delete group
        path = new Object[1];  path[0]=ROOT;
        childIdx = new int[1];  childIdx[0]=groupOrder.indexOf(yg);
        childObj = new Object[1];  childObj[0]=yg;
        groups.remove(yg);  groupOrder.removeElement(yg);
      }
      else
      // -----Send event to delete user in group
        path = new Object[2];  path[0]=ROOT;  path[1]=yg;
        childIdx = new int[1];  childIdx[0]=v.indexOf(yu);
        childObj = new Object[1];  childObj[0]=yu;
        v.removeElement(yu);
      }
      TreeModelEvent tme = new TreeModelEvent(this,path,childIdx,childObj);
      new FireEvent(tme,2);
    }
    void updateUser(YahooUser yu,String yg)
    {  // -----Send out an event for every occurance of yu in every group
      for(int i=0;i<groupOrder.size();i++)
      {  Vector v=(Vector)groups.get(groupOrder.elementAt(i));
        int idx=v.indexOf(yu);
        if(idx>=0)
        {  Object[] path = new Object[2];  path[0]=ROOT;  path[1]=groupOrder.elementAt(i);
          int[] childIdx = new int[1];  childIdx[0]=idx;
          Object[] childObj = new Object[1];  childObj[0]=yu;
          TreeModelEvent tme = new TreeModelEvent(this,path,childIdx,childObj);
          new FireEvent(tme,3);
        }
      }
    }
    void updateAll()
    {  // -----Send event to update entire tree, from root
      Object[] path = new Object[1];
      path[0]=ROOT;
      TreeModelEvent tme = new TreeModelEvent(this,path);
      new FireEvent(tme,4);
    }

    // -------------------------------------------------------------
    // This is called by the FireEvent inner class of the super class
    // -------------------------------------------------------------
    void fireEvent(Object listener,EventObject ev,int mode)
    {  TreeModelListener l = (TreeModelListener)listener;
      TreeModelEvent tme = (TreeModelEvent)ev;
      switch(mode)
      {  case 1 :  l.treeNodesInserted(tme)break;
        case 2 :  l.treeNodesRemoved(tme)break;
        case 3 :  l.treeNodesChanged(tme)break;
        case 4 :  l.treeStructureChanged(tme)break;
        default return;
      }
    }

    // -------------------------------------------------------------
    // Tree model methods.  Return types are:
    //   'ROOT' (special static String) if root of tree.
    //   'YahooGroup' for children of the root
    //   'YahooUser' for children of a group
    // -------------------------------------------------------------
    public void addTreeModelListener(TreeModelListener l) { addListener(l); }
    public void removeTreeModelListener(TreeModelListener l) { removeListener(l); }
    public Object getChild(Object parent,int index)
    {  if(parent==ROOTreturn  groupOrder.elementAt(index);
      else if(parent instanceof Stringreturn _get(parent).elementAt(index);
      else  return null;
    }
    public int getChildCount(Object parent)
    {  if(parent==ROOTreturn  groupOrder.size();
      else if(parent instanceof Stringreturn _get(parent).size();
      else  return -1;
    }
    public int getIndexOfChild(Object parent,Object child)
    {  if(parent==ROOTreturn groupOrder.indexOf(child);
      else if(parent instanceof Stringreturn _get(parent).indexOf(child);
      else  return -1;
    }
    public Object getRoot() { return ROOT; }
    public boolean isLeaf(Object node) { return (node instanceof YahooUser); }
    public void valueForPathChanged(TreePath path,Object newValue) {}

    // -----Util
    private Vector _get(Object gr) { return (Vector)groups.get(gr); }
  }

  // *****************************************************************
  // Handy empty model
  // *****************************************************************
  static class DummyListModel implements ListModel
  public void addListDataListener(ListDataListener l) {}
    public void removeListDataListener(ListDataListener l) {}
    public int getSize() { return 0; }
    public Object getElementAt(int idx) { return null; }
  }


  // *****************************************************************
  // Compares any two YahooUser and YahooChatUser objects.
  // *****************************************************************
  static class UserComparator implements Comparator
  public int compare(Object o1,Object o2) throws ClassCastException
    String s1,s2;
      // -----Object 1
      if(o1 instanceof YahooUsers1=((YahooUser)o1).getId();
      else if(o1 instanceof YahooChatUsers1=((YahooChatUser)o1).getId();
      else throw new ClassCastException("Incompatable types");
      // -----Object 2
      if(o2 instanceof YahooUsers2=((YahooUser)o2).getId();
      else if(o2 instanceof YahooChatUsers2=((YahooChatUser)o2).getId();
      else throw new ClassCastException("Incompatable types");
      // -----Compare
      s1=s1.toLowerCase();  s2=s2.toLowerCase();
      return s1.compareTo(s2);
    }

    public boolean equals(Object o) { return (o==this); }
  }

  // *****************************************************************
  // Compares any two YahooGroup objects.
  // *****************************************************************
  static class GroupComparator implements Comparator
  public int compare(Object o1,Object o2) throws ClassCastException
    /*YahooGroup g1 = (YahooGroup)o1;
      YahooGroup g2 = (YahooGroup)o2;
      String s1=g1.getName().toLowerCase();
      String s2=g2.getName().toLowerCase();*/
      String s1 = ((String)o1).toLowerCase();
      String s2 = ((String)o2).toLowerCase();
      return s1.compareTo(s2);
    }
  }
 
  // *****************************************************************
  // Cell renderer class for friends list type component.
  // *****************************************************************
  /*class GroupTreeCellRenderer extends JLabel implements TreeCellRenderer
  { 
    public Component getTreeCellRendererComponent(JTree tree,Object value,
      boolean selected,boolean expanded,boolean leaf,int row,boolean focus)
    { 
    } 
  }*/

  // *****************************************************************
  // This thread cleans up references in the models hashtable which
  // are no longer needed.
  // *****************************************************************
  class QueueHandler extends Thread
  private boolean quitFlag=false;

    QueueHandler()
    {  super(factoryThreads,"Weak Ref. Queue");
      this.setPriority(Thread.MIN_PRIORITY)this.start();
    }

    public void run()
    {  try
      Reference ref=queue.remove()// Blocks
        while(!quitFlag && ref!=null)
        {  for(Enumeration e=models.keys();e.hasMoreElements();)
          {  Object k = e.nextElement();
            if(models.get(k)==refmodels.remove(k);
          }
          ref=queue.remove();      // Blocks
        }
      }catch(InterruptedException e) {}
    }

    public void quit()
    {  quitFlag=truethis.interrupt();
    }
  }
}
TOP

Related Classes of ymsg.support.SwingModelFactory$AbstractUserModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.