File Source: ChannelInfo.java

         /* 
    P/P   *  Method: com.dmdirc.parser.irc.ChannelInfo__static_init
          */
     1  /*
     2   * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy
     5   * of this software and associated documentation files (the "Software"), to deal
     6   * in the Software without restriction, including without limitation the rights
     7   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   * copies of the Software, and to permit persons to whom the Software is
     9   * furnished to do so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in
    12   * all copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  
    23  package com.dmdirc.parser.irc;
    24  
    25  import java.util.ArrayList;
    26  import java.util.Hashtable;
    27  import java.util.HashMap;
    28  import java.util.LinkedList;
    29  import java.util.List;
    30  import java.util.Map;
    31  import java.util.Queue;
    32  
    33  /**
    34   * Contains Channel information.
    35   * 
    36   * @author Shane Mc Cormack
    37   * @author Chris Smith
    38   * @see IRCParser
    39   */
    40  public class ChannelInfo {
    41  	/**
    42  	 * Boolean repreenting the status of names requests.
    43  	 * When this is false, any new names reply will cause current known channelclients to be removed.
    44  	 */
    45  	private boolean bAddingNames = true;
    46  	
    47  	/** Unixtimestamp representing time when the channel was created. */
    48  	private long nCreateTime = 0;
    49  	
    50  	/** Current known topic in the channel. */
    51  	private String sTopic = "";
    52  	/** Last known user to set the topic (Full host where possible). */
    53  	private String sTopicUser = "";
    54  	/** Unixtimestamp representing time when the topic was set. */
    55  	private long nTopicTime = 0;
    56  	
    57  	/** Known boolean-modes for channel. */
    58  	private long nModes;
    59  	/** Reference to the parser object that owns this channel, Used for modes. */
    60  	private final IRCParser myParser; // Reference to parser object that owns this channel. Used for Modes
    61  	
    62  	/** Channel Name. */
    63  	private final String sName;
    64  	
    65  	/** Hashtable containing references to ChannelClients. */
    66  	private final Map<String, ChannelClientInfo> hChannelUserList = new Hashtable<String, ChannelClientInfo>();
    67  	/** Hashtable storing values for modes set in the channel that use parameters. */
    68  	private final Map<Character, String> hParamModes = new Hashtable<Character, String>();
    69  	/** Hashtable storing list modes. */
    70  	private final Map<Character, ArrayList<ChannelListModeItem>> hListModes = new Hashtable<Character, ArrayList<ChannelListModeItem>>();
    71  	/**
    72  	 * LinkedList storing status of mode adding.
    73  	 * if an item is in this list for a mode, we are expecting new items for the list
    74  	 */
    75  	private final List<Character> lAddingModes = new LinkedList<Character>();
    76  	/** Modes waiting to be sent to the server. */
    77  	private final List<String> lModeQueue = new LinkedList<String>();
    78  	/** A Map to allow applications to attach misc data to this object */
    79  	private Map myMap;
    80  	
    81  	/** Queue of requested list modes */
    82  	private final Queue<Character> listModeQueue = new LinkedList<Character>();
    83  	/** Listmode Queue Time */
    84  	private long listModeQueueTime = System.currentTimeMillis();
    85  	/** Have we asked the server for the list modes for this channel yet? */
    86  	private boolean askedForListModes = false;
    87  	/** Has OnChannelGotListModes ever been called for this channel? */
    88  	private boolean hasGotListModes = false;
    89  
    90  	/**
    91  	 * Create a new channel object.
    92  	 *
    93  	 * @param tParser Refernce to parser that owns this channelclient (used for modes)	 
    94  	 * @param name Channel name.
    95  	 */
        	 /* 
    P/P 	  *  Method: void com.dmdirc.parser.irc.ChannelInfo(IRCParser, String)
        	  * 
        	  *  Postconditions:
        	  *    this.askedForListModes == 0
        	  *    this.hasGotListModes == 0
        	  *    this.nCreateTime == 0
        	  *    this.nTopicTime == 0
        	  *    this.bAddingNames == 1
        	  *    new HashMap(ChannelInfo#7) num objects == 1
        	  *    new Hashtable(ChannelInfo#1) num objects == 1
        	  *    new Hashtable(ChannelInfo#2) num objects == 1
        	  *    new Hashtable(ChannelInfo#3) num objects == 1
        	  *    new LinkedList(ChannelInfo#4) num objects == 1
        	  *    ...
        	  */
    96  	public ChannelInfo(final IRCParser tParser, final String name) {
    97  		myMap = new HashMap<Object, Object>();
    98  		myParser = tParser;
    99  		sName = name;
   100  	}
   101  	
   102  	/**
   103  	 * Get the listModeQueue.
   104  	 *
   105  	 * @return The listModeQueue
   106  	 */
   107  	public Queue<Character> getListModeQueue() {
        		 /* 
    P/P 		  *  Method: Queue getListModeQueue()
        		  * 
        		  *  Preconditions:
        		  *    init'ed(this.listModeQueueTime)
        		  *    (soft) this.myParser != null
        		  *    (soft) this.myParser.myCallbackManager != null
        		  *    (soft) this.myParser.myCallbackManager.callbackHash != null
        		  * 
        		  *  Postconditions:
        		  *    return_value == One-of{this.listModeQueue, &amp;new LinkedList(getListModeQueue#1)}
        		  *    init'ed(return_value)
        		  *    init'ed(this.listModeQueueTime)
        		  *    new LinkedList(getListModeQueue#1) num objects <= 1
        		  */
   108  		Queue<Character> result = listModeQueue;
   109  		final long now = System.currentTimeMillis();
   110  		// Incase of breakage, if getListModeQueue() was last called greater than
   111  		// 60 seconds ago, we reset the list.
   112  		if (now-(30*1000) > listModeQueueTime) {
   113  			result = new LinkedList<Character>();
   114  			myParser.callDebugInfo(IRCParser.DEBUG_LMQ, "Resetting LMQ");
   115  		}
   116  		listModeQueueTime = now;
   117  		return result;
   118  	}
   119  	
   120  	/**
   121  	 * Ask the server for all the list modes for this channel.
   122  	 */
   123  	public void requestListModes() {
        		 /* 
    P/P 		  *  Method: void requestListModes()
        		  * 
        		  *  Preconditions:
        		  *    this.hChannelUserList != null
        		  *    this.myParser != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.myParser.cMyself != null
        		  *    (soft) init'ed(this.myParser.currentSocketState)
        		  *    (soft) this.myParser.h005Info != null
        		  *    (soft) this.myParser.hChanModesOther != null
        		  *    (soft) this.myParser.hChannelList != null
        		  *    (soft) this.myParser.hPrefixModes != null
        		  *    (soft) this.myParser.myCallbackManager != null
        		  *    ...
        		  * 
        		  *  Presumptions:
        		  *    java.util.Iterator:next(...)@159 != null
        		  *    java.util.Map:get(...)@141 != null
        		  *    java.util.Map:get(...)@160 != null
        		  *    java.util.Map:keySet(...)@159 != null
        		  *    me.myParser@124 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.String:substring(...)._tainted)
        		  *    possibly_updated(this.askedForListModes)
        		  *    possibly_updated(this.myParser.cMyself.myAwayReason)
        		  *    init'ed(this.myParser.stringConverter)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1) num objects)
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    init'ed(new char[](IRCStringConverter#1) num objects)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.Byte:byteValue(...)@160: {-128..0, 2..255}, {1}
        		  *    java.lang.Character:charValue(...)@162: {101}, {0..100, 102..216-1}
        		  *    java.lang.Character:charValue(...)@162: {0..72, 74..216-1}, {73}
        		  *    java.lang.Character:charValue(...)@165: {0..71, 73..216-1}, {72}
        		  *    java.lang.String:equals(...)@134: {1}, {0}
        		  *    java.lang.String:equals(...)@135: {0}, {1}
        		  *    java.lang.String:equals(...)@137: {0}, {1}
        		  *    java.util.Iterator:hasNext(...)@159: {0}, {1}
        		  *    java.util.Map:containsKey(...)@155: {1}, {0}
        		  *    java.util.Map:get(...)@141: Addr_Set{null}, Inverse{null}
        		  */
   124  		final ChannelClientInfo me = getUser(myParser.getMyself());
   125  		if (me == null) {
   126  			// In a normal situation of non bouncer-brokenness this won't happen
   127  			return;
   128  		}
   129  		
   130  		askedForListModes = true;
   131  		
   132  		final String thisIRCD = myParser.getIRCD(true).toLowerCase();
   133  		final boolean isFreenode = (thisIRCD.equals("hyperion") || thisIRCD.equals("dancer"));
   134  		final boolean isUnreal = thisIRCD.equals("unreal");
   135  		final boolean isStarChat = thisIRCD.equals("starchat");
   136  		final boolean isHybrid = thisIRCD.equals("hybrid") || thisIRCD.equals("oftc-hybrid");
   137  		final boolean isCharybdis = thisIRCD.equals("charybdis");
   138  		
   139  		// We are considered opped if we have a mode higher than voice (or if we have any modes if voice doesn't exist)
   140  		long voiceValue = 0;
   141  		if (myParser.hPrefixModes.get('v') != null) { voiceValue = myParser.hPrefixModes.get('v');}
   142  		final boolean isOpped = me.getImportantModeValue() > voiceValue;
   143  		
   144  		int modecount = 1;
   145  		if (!isUnreal) {
   146  			try { 
   147  				modecount = Integer.parseInt(myParser.h005Info.get("MODES"));
   148  			} catch (NumberFormatException e) { /* use default modecount */}
   149  		}
   150  		
   151  		// Support for potential future decent mode listing in the protocol
   152  		//
   153  		// See my proposal: http://shane.dmdirc.com/listmodes.php
   154  		// Add listmode handler
   155  		final boolean supportLISTMODE = myParser.h005Info.containsKey("LISTMODE");
   156  		
   157  		String listmodes = "";
   158  		int i = 0;
   159  		for (Character cTemp : myParser.hChanModesOther.keySet()) {
   160  			final int nTemp = myParser.hChanModesOther.get(cTemp);
   161  			if (nTemp == IRCParser.MODE_LIST) {
   162  				if ((isFreenode || isHybrid || isCharybdis) && (cTemp == 'e' || cTemp == 'I') && !isOpped) {
   163  					// IRCD doesn't allow non-ops to ask for these modes.
   164  					continue;
   165  				} else if (isStarChat && cTemp == 'H') {
   166  					// IRCD Denies the mode exists
   167  					continue;
   168  				}
   169  				i++;
   170  				listmodes = listmodes + cTemp;
   171  				if (i >= modecount && !supportLISTMODE) {
   172  					myParser.sendString("MODE "+getName()+" "+listmodes);
   173  					i = 0;
   174  					listmodes = "";
   175  				}
   176  			}
   177  		}
   178  		if (i > 0) {
   179  			if (supportLISTMODE) {
   180  				myParser.sendString("LISTMODE "+getName()+" "+listmodes);
   181  			} else {
   182  				myParser.sendString("MODE "+getName()+" "+listmodes);
   183  			}
   184  		}
   185  	}
   186  	
   187  	/**
   188  	 * Have we ever asked the server for this channels listmodes?
   189  	 *
   190  	 * @return True if requestListModes() has ever been used, else false
   191  	 */
   192  	public boolean hasAskedForListModes() {
        		 /* 
    P/P 		  *  Method: bool hasAskedForListModes()
        		  * 
        		  *  Preconditions:
        		  *    init'ed(this.askedForListModes)
        		  * 
        		  *  Postconditions:
        		  *    return_value == this.askedForListModes
        		  *    init'ed(return_value)
        		  */
   193  		return askedForListModes;
   194  	}
   195  	
   196  	/**
   197  	 * Returns true if OnChannelGotListModes ever been called for this channel.
   198  	 *
   199  	 * @return True if OnChannelGotListModes ever been called for this channel.
   200  	 */
   201  	public boolean hasGotListModes() {
        		 /* 
    P/P 		  *  Method: bool hasGotListModes()
        		  * 
        		  *  Preconditions:
        		  *    init'ed(this.hasGotListModes)
        		  * 
        		  *  Postconditions:
        		  *    return_value == this.hasGotListModes
        		  *    init'ed(return_value)
        		  */
   202  		return hasGotListModes;
   203  	}
   204  	
   205  	/**
   206  	 * Set if OnChannelGotListModes ever been called for this channel.
   207  	 *
   208  	 * @param newValue new value for if OnChannelGotListModes ever been called for this channel.
   209  	 */
   210  	protected void setHasGotListModes(final boolean newValue) {
        		 /* 
    P/P 		  *  Method: void setHasGotListModes(bool)
        		  * 
        		  *  Postconditions:
        		  *    this.hasGotListModes == newValue
        		  *    init'ed(this.hasGotListModes)
        		  */
   211  		hasGotListModes = newValue;
   212  	}
   213  	
   214  	/**
   215  	 * Set the Map object attatched to this object.
   216  	 *
   217  	 * @param newMap New Map to attatch.
   218  	 */
   219  	public void setMap(final Map newMap) {
        		 /* 
    P/P 		  *  Method: void setMap(Map)
        		  * 
        		  *  Postconditions:
        		  *    this.myMap == newMap
        		  *    init'ed(this.myMap)
        		  */
   220  		myMap = newMap;
   221  	}
   222  	
   223  	/**
   224  	 * Get the Map object attatched to this object.
   225  	 *
   226  	 * @return Map to attatched to this.
   227  	 */
   228  	public Map getMap() {
        		 /* 
    P/P 		  *  Method: Map getMap()
        		  * 
        		  *  Preconditions:
        		  *    init'ed(this.myMap)
        		  * 
        		  *  Postconditions:
        		  *    return_value == this.myMap
        		  *    init'ed(return_value)
        		  */
   229  		return myMap;
   230  	}
   231  	
   232  	/**
   233  	 * Set if we are getting a names request or not.
   234  	 *
   235  	 * @param newValue if false, any new names reply will cause current known channelclients to be removed.
   236  	 */
        	 /* 
    P/P 	  *  Method: void setAddingNames(bool)
        	  * 
        	  *  Postconditions:
        	  *    this.bAddingNames == newValue
        	  *    init'ed(this.bAddingNames)
        	  */
   237  	public void setAddingNames(final boolean newValue) { bAddingNames = newValue; }
   238  	
   239  	/**
   240  	 * Get if we are getting a names request or not.
   241  	 *
   242  	 * @return if false, any new names reply will cause current known channelclients to be removed.
   243  	 */
        	 /* 
    P/P 	  *  Method: bool isAddingNames()
        	  * 
        	  *  Preconditions:
        	  *    init'ed(this.bAddingNames)
        	  * 
        	  *  Postconditions:
        	  *    return_value == this.bAddingNames
        	  *    init'ed(return_value)
        	  */
   244  	public boolean isAddingNames() { return bAddingNames; }
   245  	
   246  	/**
   247  	 * Get the name of this channel object.
   248  	 *
   249  	 * @return Channel name.
   250  	 */	
        	 /* 
    P/P 	  *  Method: String getName()
        	  * 
        	  *  Postconditions:
        	  *    return_value == this.sName
        	  *    init'ed(return_value)
        	  */
   251  	public String getName() { return sName; }
   252  	/**
   253  	 * Get the number of users known on this channel.
   254  	 *
   255  	 * @return Channel user count.
   256  	 */
        	 /* 
    P/P 	  *  Method: int getUserCount()
        	  * 
        	  *  Preconditions:
        	  *    this.hChannelUserList != null
        	  * 
        	  *  Postconditions:
        	  *    init'ed(return_value)
        	  */
   257  	public int getUserCount() { return hChannelUserList.size(); }
   258  	
   259  	/**
   260  	 * Get the channel users.
   261  	 *
   262  	 * @return ArrayList of ChannelClients
   263  	 */
   264  	public List<ChannelClientInfo> getChannelClients() {
        		 /* 
    P/P 		  *  Method: List getChannelClients()
        		  * 
        		  *  Preconditions:
        		  *    this.hChannelUserList != null
        		  * 
        		  *  Postconditions:
        		  *    return_value == &amp;new ArrayList(getChannelClients#1)
        		  *    new ArrayList(getChannelClients#1) num objects == 1
        		  */
   265  		return new ArrayList<ChannelClientInfo>(hChannelUserList.values());
   266  	}
   267  	
   268  	/**
   269  	 * Empty the channel (Remove all known channelclients).
   270  	 */
   271  	protected void emptyChannel() {
        		 /* 
    P/P 		  *  Method: void emptyChannel()
        		  * 
        		  *  Preconditions:
        		  *    this.hChannelUserList != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.myParser != null
        		  *    (soft) init'ed(this.myParser.cMyself)
        		  *    (soft) this.myParser.hClientList != null
        		  *    (soft) this.myParser.stringConverter.lowercase != null
        		  *    (soft) init'ed(this.myParser.stringConverter.lowercase[...])
        		  * 
        		  *  Presumptions:
        		  *    cTemp.myChannelClientInfos@273 != null
        		  *    cTemp.myParser.stringConverter.lowercase@273 != null
        		  *    cTemp.myParser@273 != null
        		  *    cTemp.sNickname != null
        		  *    client.cClient@273 != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(this.myParser.stringConverter)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1) num objects)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    init'ed(new char[](IRCStringConverter#1) num objects)
        		  *    init'ed(new char[](IRCStringConverter#1).length)
        		  *    init'ed(new char[](IRCStringConverter#1)[...])
        		  *    init'ed(new char[](IRCStringConverter#2) num objects)
        		  *    init'ed(new char[](IRCStringConverter#2).length)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Iterator:hasNext(...)@273: {0}, {1}
        		  */
   272  		ClientInfo cTemp = null;
   273  		for (ChannelClientInfo client : hChannelUserList.values()) {
   274  			cTemp = client.getClient();
   275  			cTemp.delChannelClientInfo(client);
   276  			if (cTemp != myParser.getMyself() && !cTemp.checkVisibility()) {
   277  				myParser.removeClient(cTemp);
   278  			}
   279  		}
   280  		hChannelUserList.clear();
   281  	}
   282  
   283  	/**
   284  	 * Get the ChannelClientInfo object associated with a nickname.
   285  	 *
   286  	 * @param sWho Nickname to return channelclient for
   287  	 * @return ChannelClientInfo object requested, or null if not found
   288  	 */
   289  	public ChannelClientInfo getUser(final String sWho) {
        		 /* 
    P/P 		  *  Method: ChannelClientInfo getUser(String)
        		  * 
        		  *  Preconditions:
        		  *    init'ed(this.myParser.stringConverter)
        		  *    sWho != null
        		  *    this.hChannelUserList != null
        		  *    this.myParser != null
        		  *    (soft) this.myParser.stringConverter.lowercase != null
        		  *    (soft) init'ed(this.myParser.stringConverter.lowercase[...])
        		  *    (soft) this.sName != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  *    this.myParser.stringConverter != null
        		  *    new ChannelClientInfo(getUser#1*) num objects == 0
        		  *    new ClientInfo(getUser#2*) num objects == 0
        		  *    new HashMap(ChannelClientInfo#1) num objects == 0
        		  *    new HashMap(ClientInfo#3) num objects == 0
        		  *    new Hashtable(ClientInfo#1) num objects == 0
        		  *    new LinkedList(ClientInfo#2) num objects == 0
        		  *    not_init'ed(new ChannelClientInfo(getUser#1*).cClient)
        		  *    not_init'ed(new ChannelClientInfo(getUser#1*).myChannel)
        		  *    ...
        		  */
   290  		return getUser(sWho, false);
   291  	}
   292  	
   293  	/**
   294  	 * Get the ChannelClientInfo object associated with a nickname.
   295  	 *
   296  	 * @param sWho Nickname to return channelclient for
   297  	 * @param createFake Create a fake client if not found
   298  	 * @return ChannelClientInfo object requested
   299  	 * @since 0.6
   300  	 */
   301  	public ChannelClientInfo getUser(final String sWho, final boolean createFake) {
        		 /* 
    P/P 		  *  Method: ChannelClientInfo getUser(String, bool)
        		  * 
        		  *  Preconditions:
        		  *    init'ed(this.myParser.stringConverter)
        		  *    sWho != null
        		  *    this.hChannelUserList != null
        		  *    this.myParser != null
        		  *    (soft) this.myParser.stringConverter.lowercase != null
        		  *    (soft) init'ed(this.myParser.stringConverter.lowercase[...])
        		  *    (soft) this.sName != null
        		  * 
        		  *  Presumptions:
        		  *    setFake(...).myParser.stringConverter.lowercase != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  *    this.myParser.stringConverter != null
        		  *    new ChannelClientInfo(getUser#1) num objects <= 1
        		  *    new ChannelClientInfo(getUser#1).cClient == &amp;new ClientInfo(getUser#2)
        		  *    new ChannelClientInfo(getUser#1).myChannel == this
        		  *    new ChannelClientInfo(getUser#1).myChannel != null
        		  *    new ChannelClientInfo(getUser#1).myMap == &amp;new HashMap(ChannelClientInfo#1)
        		  *    new ChannelClientInfo(getUser#1).myParser == this.myParser
        		  *    new ChannelClientInfo(getUser#1).myParser != null
        		  *    new ClientInfo(getUser#2) num objects <= 1
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    createFake: {0}, {1}
        		  *    java.util.Map:containsKey(...)@303: {0}, {1}
        		  */
   302  		final String who = myParser.getIRCStringConverter().toLowerCase(ClientInfo.parseHost(sWho));
   303  		if (hChannelUserList.containsKey(who)) {
   304  			return hChannelUserList.get(who);
   305  		}
   306  		if (createFake) {
   307  			return new ChannelClientInfo(myParser, (new ClientInfo(myParser, sWho)).setFake(true), this);
   308  		} else {
   309  			return null;
   310  		}
   311  	}
   312  	
   313  	/**
   314  	 * Get the ChannelClientInfo object associated with a ClientInfo object.
   315  	 *
   316  	 * @param cWho ClientInfo to return ChannelClient for
   317  	 * @return ChannelClientInfo object requested, or null if not found
   318  	 */	
   319  	public ChannelClientInfo getUser(final ClientInfo cWho) {
        		 /* 
    P/P 		  *  Method: ChannelClientInfo getUser(ClientInfo)
        		  * 
        		  *  Preconditions:
        		  *    this.hChannelUserList != null
        		  * 
        		  *  Presumptions:
        		  *    java.util.Iterator:next(...)@320 != null
        		  *    java.util.Map:values(...)@320 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Iterator:hasNext(...)@320: {0}, {1}
        		  */
   320  		for (ChannelClientInfo client : hChannelUserList.values()) {
   321  			if (client.getClient() == cWho) {
   322  				return client;
   323  			}
   324  		}
   325  		return null;
   326  	}
   327  	
   328  	/**
   329  	 * Get the ChannelClientInfo object associated with a ClientInfo object.
   330  	 *
   331  	 * @param cClient Client object to be added to channel
   332  	 * @return ChannelClientInfo object added, or an existing object if already known on channel
   333  	 */
   334  	protected ChannelClientInfo addClient(final ClientInfo cClient) {
        		 /* 
    P/P 		  *  Method: ChannelClientInfo addClient(ClientInfo)
        		  * 
        		  *  Preconditions:
        		  *    (soft) cClient != null
        		  *    (soft) cClient.myChannelClientInfos != null
        		  *    (soft) cClient.myParser != null
        		  *    (soft) init'ed(cClient.myParser.stringConverter)
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.hChannelUserList != null
        		  *    (soft) this.myParser != null
        		  *    (soft) this.sName != null
        		  * 
        		  *  Presumptions:
        		  *    cClient.myParser.stringConverter.lowercase@335 != null
        		  *    cTemp.cClient.sNickname != null
        		  *    getIRCStringConverter(...).lowercase != null
        		  * 
        		  *  Postconditions:
        		  *    cClient.myParser.stringConverter == One-of{old cClient.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1), old this.myParser.stringConverter}
        		  *    init'ed(cClient.myParser.stringConverter)
        		  *    return_value != null
        		  *    this.myParser.stringConverter == One-of{old this.myParser.stringConverter, old cClient.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1)}
        		  *    init'ed(this.myParser.stringConverter)
        		  *    new ChannelClientInfo(addClient#1) num objects <= 1
        		  *    new ChannelClientInfo(addClient#1).cClient == cClient
        		  *    new ChannelClientInfo(addClient#1).cClient != null
        		  *    new ChannelClientInfo(addClient#1).myChannel == this
        		  *    new ChannelClientInfo(addClient#1).myChannel != null
        		  *    ...
        		  */
   335  		ChannelClientInfo cTemp = getUser(cClient);
   336  		if (cTemp == null) { 
   337  			cTemp = new ChannelClientInfo(myParser, cClient, this);
   338  			hChannelUserList.put(myParser.getIRCStringConverter().toLowerCase(cTemp.getNickname()), cTemp);
   339  		}
   340  		return cTemp;
   341  	}
   342  	
   343  	/**
   344  	 * Remove ChannelClientInfo object associated with a ClientInfo object.
   345  	 *
   346  	 * @param cClient Client object to be removed from channel
   347  	 */	
   348  	protected void delClient(final ClientInfo cClient) {
        		 /* 
    P/P 		  *  Method: void delClient(ClientInfo)
        		  * 
        		  *  Preconditions:
        		  *    this.hChannelUserList != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.myParser != null
        		  *    (soft) init'ed(this.myParser.cMyself)
        		  *    (soft) this.myParser.hClientList != null
        		  * 
        		  *  Presumptions:
        		  *    cTemp.cClient.sNickname != null
        		  *    cTemp.myChannel.sName@350 != null
        		  *    cTemp.myChannel@350 != null
        		  *    clTemp.myChannelClientInfos@350 != null
        		  *    clTemp.myParser.stringConverter.lowercase@350 != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(this.myParser.stringConverter)
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    new char[](IRCStringConverter#1) num objects <= 1
        		  *    init'ed(new char[](IRCStringConverter#1).length)
        		  *    possibly_updated(new char[](IRCStringConverter#1)[...])
        		  *    init'ed(new char[](IRCStringConverter#1)[...])
        		  *    new char[](IRCStringConverter#2) num objects <= 1
        		  *    ...
        		  */
   349  		ChannelClientInfo cTemp = null;
   350  		cTemp = getUser(cClient);
   351  		if (cTemp != null) {
   352  			final ClientInfo clTemp = cTemp.getClient();
   353  			clTemp.delChannelClientInfo(cTemp);
   354  			if (clTemp != myParser.getMyself() && !clTemp.checkVisibility()) {
   355  				myParser.removeClient(clTemp);
   356  			}
   357  			hChannelUserList.remove(myParser.getIRCStringConverter().toLowerCase(cTemp.getNickname()));
   358  		}
   359  	}	
   360  	
   361  	/**
   362  	 * Rename a channelClient.
   363  	 *
   364  	 * @param oldNickname Nickname client used to be known as
   365  	 * @param cChannelClient ChannelClient object with updated client object
   366  	 */	
   367  	protected void renameClient(final String oldNickname, final ChannelClientInfo cChannelClient) {
        		 /* 
    P/P 		  *  Method: void renameClient(String, ChannelClientInfo)
        		  * 
        		  *  Preconditions:
        		  *    this.hChannelUserList != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.myParser != null
        		  *    (soft) this.myParser.stringConverter.lowercase != null
        		  *    (soft) init'ed(this.myParser.stringConverter.lowercase[...])
        		  * 
        		  *  Presumptions:
        		  *    cTemp.cClient.sNickname@370 != null
        		  *    cTemp.cClient@370 != null
        		  *    java.util.Map:get(...)@370 != null
        		  * 
        		  *  Postconditions:
        		  *    this.myParser.stringConverter == One-of{old this.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1)}
        		  *    init'ed(this.myParser.stringConverter)
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    new IRCStringConverter(getIRCStringConverter#1).limit == 4
        		  *    new IRCStringConverter(getIRCStringConverter#1).lowercase == &amp;new char[](IRCStringConverter#1)
        		  *    new IRCStringConverter(getIRCStringConverter#1).uppercase == &amp;new char[](IRCStringConverter#2)
        		  *    new char[](IRCStringConverter#1) num objects <= 1
        		  *    new char[](IRCStringConverter#1).length == 127
        		  *    possibly_updated(new char[](IRCStringConverter#1)[...])
        		  *    new char[](IRCStringConverter#2) num objects <= 1
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Map:containsKey(...)@369: {0}, {1}
        		  */
   368  		ChannelClientInfo cTemp = null;
   369  		if (hChannelUserList.containsKey(oldNickname)) {
   370  			cTemp = hChannelUserList.get(oldNickname);
   371  			if (cTemp == cChannelClient) {
   372  				// Remove the old key
   373  				hChannelUserList.remove(oldNickname);
   374  				// Add with the new key. (getNickname will return the new name not the
   375  				// old one)
   376  				hChannelUserList.put(myParser.getIRCStringConverter().toLowerCase(cTemp.getNickname()), cTemp);
   377  			}
   378  		}
   379  	}
   380  	
   381  	/**
   382  	 * Set the create time.
   383  	 *
   384  	 * @param nNewTime New unixtimestamp time for the channel creation (Seconds since epoch, not milliseconds)
   385  	 */
        	 /* 
    P/P 	  *  Method: void setCreateTime(long)
        	  * 
        	  *  Postconditions:
        	  *    this.nCreateTime == nNewTime
        	  *    init'ed(this.nCreateTime)
        	  */
   386  	protected void setCreateTime(final long nNewTime) { nCreateTime = nNewTime; }
   387  	/**
   388  	 * Get the Create time.
   389  	 *
   390  	 * @return Unixtimestamp time for the channel creation (Seconds since epoch, not milliseconds)
   391  	 */
        	 /* 
    P/P 	  *  Method: long getCreateTime()
        	  * 
        	  *  Preconditions:
        	  *    init'ed(this.nCreateTime)
        	  * 
        	  *  Postconditions:
        	  *    return_value == this.nCreateTime
        	  *    init'ed(return_value)
        	  */
   392  	public long getCreateTime() { return nCreateTime; }	
   393  	
   394  	/**
   395  	 * Set the topic time.
   396  	 *
   397  	 * @param nNewTime New unixtimestamp time for the topic (Seconds since epoch, not milliseconds)
   398  	 */
        	 /* 
    P/P 	  *  Method: void setTopicTime(long)
        	  * 
        	  *  Postconditions:
        	  *    this.nTopicTime == nNewTime
        	  *    init'ed(this.nTopicTime)
        	  */
   399  	protected void setTopicTime(final long nNewTime) { nTopicTime = nNewTime; }
   400  	/**
   401  	 * Get the topic time.
   402  	 *
   403  	 * @return Unixtimestamp time for the topic (Seconds since epoch, not milliseconds)
   404  	 */
        	 /* 
    P/P 	  *  Method: long getTopicTime()
        	  * 
        	  *  Preconditions:
        	  *    init'ed(this.nTopicTime)
        	  * 
        	  *  Postconditions:
        	  *    return_value == this.nTopicTime
        	  *    init'ed(return_value)
        	  */
   405  	public long getTopicTime() { return nTopicTime; }	
   406  	
   407  	/**
   408  	 * Set the topic.
   409  	 *
   410  	 * @param sNewTopic New contents of topic
   411  	 */	
        	 /* 
    P/P 	  *  Method: void setTopic(String)
        	  * 
        	  *  Postconditions:
        	  *    this.sTopic == sNewTopic
        	  *    init'ed(this.sTopic)
        	  */
   412  	protected void setTopic(final String sNewTopic) { sTopic = sNewTopic; }
   413  	/**
   414  	 * Get the topic.
   415  	 *
   416  	 * @return contents of topic
   417  	 */	
        	 /* 
    P/P 	  *  Method: String getTopic()
        	  * 
        	  *  Preconditions:
        	  *    init'ed(this.sTopic)
        	  * 
        	  *  Postconditions:
        	  *    return_value == this.sTopic
        	  *    init'ed(return_value)
        	  */
   418  	public String getTopic() { return sTopic; }	
   419  
   420  	/**
   421  	 * Set the topic creator.
   422  	 *
   423  	 * @param sNewUser New user who set the topic (nickname if gotten on connect, full host if seen by parser)
   424  	 */	
        	 /* 
    P/P 	  *  Method: void setTopicUser(String)
        	  * 
        	  *  Postconditions:
        	  *    this.sTopicUser == sNewUser
        	  *    init'ed(this.sTopicUser)
        	  */
   425  	protected void setTopicUser(final String sNewUser) { sTopicUser = sNewUser; }
   426  	/**
   427  	 * Get the topic creator.
   428  	 *
   429  	 * @return user who set the topic (nickname if gotten on connect, full host if seen by parser)
   430  	 */	
        	 /* 
    P/P 	  *  Method: String getTopicUser()
        	  * 
        	  *  Preconditions:
        	  *    init'ed(this.sTopicUser)
        	  * 
        	  *  Postconditions:
        	  *    return_value == this.sTopicUser
        	  *    init'ed(return_value)
        	  */
   431  	public String getTopicUser() { return sTopicUser; }
   432  	
   433  	/**
   434  	 * Set the channel modes (as an integer).
   435  	 *
   436  	 * @param nNewMode new long representing channel modes. (Boolean only)
   437  	 */	
        	 /* 
    P/P 	  *  Method: void setMode(long)
        	  * 
        	  *  Postconditions:
        	  *    this.nModes == nNewMode
        	  *    init'ed(this.nModes)
        	  */
   438  	protected void setMode(final long nNewMode) { nModes = nNewMode; }
   439  	/**
   440  	 * Get the channel modes (as an integer).
   441  	 *
   442  	 * @return long representing channel modes. (Boolean only)
   443  	 */	
        	 /* 
    P/P 	  *  Method: long getMode()
        	  * 
        	  *  Preconditions:
        	  *    init'ed(this.nModes)
        	  * 
        	  *  Postconditions:
        	  *    return_value == this.nModes
        	  *    init'ed(return_value)
        	  */
   444  	public long getMode() { return nModes; }	
   445  	
   446  	/**
   447  	 * Get the channel modes (as a string representation).
   448  	 *
   449  	 * @return string representing modes. (boolean and non-list)
   450  	 */	
   451  	public String getModeStr() {
        		 /* 
    P/P 		  *  Method: String getModeStr()
        		  * 
        		  *  Preconditions:
        		  *    this.hParamModes != null
        		  *    this.myParser != null
        		  *    this.myParser.hChanModesBool != null
        		  *    init'ed(this.nModes)
        		  * 
        		  *  Presumptions:
        		  *    java.util.Iterator:next(...)@457 != null
        		  *    java.util.Iterator:next(...)@461 != null
        		  *    java.util.Map:get(...)@458 != null
        		  *    java.util.Map:get(...)@462 != null
        		  *    java.util.Map:keySet(...)@457 != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
        		  *    return_value == &amp;java.lang.StringBuilder:toString(...)
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:isEmpty(...)@463: {1}, {0}
        		  *    java.util.Iterator:hasNext(...)@457: {0}, {1}
        		  *    java.util.Iterator:hasNext(...)@461: {0}, {1}
        		  */
   452  		final StringBuilder sModes = new StringBuilder("+");
   453  		final StringBuilder sModeParams = new StringBuilder();
   454  		String sTemp = "";
   455  		long nTemp = 0;
   456  		final long nChanModes = this.getMode();
   457  		for (char cTemp : myParser.hChanModesBool.keySet()) {
   458  			nTemp = myParser.hChanModesBool.get(cTemp);
   459  			if ((nChanModes & nTemp) == nTemp) { sModes.append(cTemp); }
   460  		}
   461  		for (char cTemp : hParamModes.keySet()) {
   462  			sTemp = hParamModes.get(cTemp);
   463  			if (!sTemp.isEmpty()) {
   464  				sModes.append(cTemp);
   465  				sModeParams.append(" ").append(this.getModeParam(cTemp));
   466   			}
   467  		}
   468  		
   469  		return sModes.append(sModeParams).toString();
   470  	}	
   471  	
   472  	/**
   473  	 * Set a channel mode that requires a parameter.
   474  	 *
   475  	 * @param cMode Character representing mode
   476  	 * @param sValue String repreenting value (if "" mode is unset)
   477  	 */	
   478  	protected void setModeParam(final Character cMode, final String sValue) { 
        		 /* 
    P/P 		  *  Method: void setModeParam(Character, String)
        		  * 
        		  *  Preconditions:
        		  *    sValue != null
        		  *    this.hParamModes != null
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:isEmpty(...)@479: {0}, {1}
        		  *    java.util.Map:containsKey(...)@480: {0}, {1}
        		  */
   479  		if (sValue.isEmpty()) {
   480  			if (hParamModes.containsKey(cMode)) {
   481  				hParamModes.remove(cMode);
   482  			}
   483  		} else {
   484  			hParamModes.put(cMode, sValue);
   485  		}
   486  	}
   487  	/**
   488  	 * Get the value of a mode that requires a parameter.
   489  	 *
   490  	 * @param cMode Character representing mode
   491  	 * @return string representing the value of the mode ("" if mode not set)
   492  	 */	
   493  	public String getModeParam(final Character cMode) { 
        		 /* 
    P/P 		  *  Method: String getModeParam(Character)
        		  * 
        		  *  Preconditions:
        		  *    this.hParamModes != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  * 
        		  *  Test Vectors:
        		  *    java.util.Map:containsKey(...)@494: {0}, {1}
        		  */
   494  		if (hParamModes.containsKey(cMode)) { 
   495  			return hParamModes.get(cMode); 
   496  		}
   497  		return "";
   498  	}
   499  	
   500  	/**
   501  	 * Add/Remove a value to a channel list.
   502  	 *
   503  	 * @param givenMode Character representing mode
   504  	 * @param givenItem ChannelListModeItem representing the item
   505  	 * @param bAdd Add or remove the value. (true for add, false for remove)
   506  	 */
   507  	protected void setListModeParam(final Character givenMode, final ChannelListModeItem givenItem, final boolean bAdd) { 
        		 /* 
    P/P 		  *  Method: void setListModeParam(Character, ChannelListModeItem, bool)
        		  * 
        		  *  Preconditions:
        		  *    this.myParser != null
        		  *    this.myParser.hChanModesOther != null
        		  *    (soft) givenItem != null
        		  *    (soft) givenItem.myItem != null
        		  *    (soft) givenItem.myOwner != null
        		  *    (soft) givenMode != null
        		  *    (soft) this.myParser.stringConverter != null
        		  *    (soft) this.hListModes != null
        		  *    (soft) this.myParser.h005Info != null
        		  *    (soft) this.myParser.sNetworkName != null
        		  *    ...
        		  * 
        		  *  Presumptions:
        		  *    getIRCStringConverter(...).lowercase.length@514 >= 1
        		  *    getIRCStringConverter(...).lowercase@514 != null
        		  *    java.util.ArrayList:get(...)@532 != null
        		  *    java.util.Map:get(...)@510 != null
        		  *    java.util.Map:get(...)@530 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(this.myParser.stringConverter)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1) num objects)
        		  *    possibly_updated(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    possibly_updated(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    possibly_updated(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    init'ed(new char[](IRCStringConverter#1) num objects)
        		  *    possibly_updated(new char[](IRCStringConverter#1).length)
        		  *    possibly_updated(new char[](IRCStringConverter#1)[...])
        		  *    init'ed(new char[](IRCStringConverter#2) num objects)
        		  *    possibly_updated(new char[](IRCStringConverter#2).length)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    bAdd: {0}, {1}
        		  *    java.lang.Byte:byteValue(...)@510: {1}, {-128..0, 2..255}
        		  *    java.lang.Character:charValue(...)@513: {98}, {0..97, 99..216-1}
        		  *    java.lang.Character:charValue(...)@513: {0..112, 114..216-1}, {113}
        		  *    java.lang.Character:charValue(...)@516: {0..97, 99..216-1}, {98}
        		  *    java.lang.Character:charValue(...)@518: {0..112, 114..216-1}, {113}
        		  *    java.lang.String:charAt(...)@516: {0..36, 38..216-1}, {37}
        		  *    java.lang.String:charAt(...)@518: {37}, {0..36, 38..216-1}
        		  *    java.lang.String:charAt(...)@521: {0..36, 38..216-1}, {37}
        		  *    java.lang.String:equals(...)@515: {1}, {0}
        		  *    ...
        		  */
   508  		Character cMode = givenMode;
   509  		ChannelListModeItem newItem = givenItem;
   510  		if (!myParser.hChanModesOther.containsKey(cMode) || myParser.hChanModesOther.get(cMode) != IRCParser.MODE_LIST) { return; }
   511  		
   512  		// Hyperion sucks.
   513  		if (cMode == 'b' || cMode == 'q') {
   514  			final String thisIRCD = myParser.getIRCD(true).toLowerCase();
   515  			if ((thisIRCD.equals("hyperion") || thisIRCD.equals("dancer"))) {
   516  				if (cMode == 'b' && givenItem.getItem().charAt(0) == '%') {
   517  					cMode = 'q';
   518  				} else if (cMode == 'q' && givenItem.getItem().charAt(0) != '%') {
   519  					cMode = 'b';
   520  				}
   521  				if (givenItem.getItem().charAt(0) == '%') {
   522  					newItem = new ChannelListModeItem(givenItem.getItem().substring(1), givenItem.getOwner(), givenItem.getTime());
   523  				}
   524  			}
   525  		}
   526  		
   527  		if (!hListModes.containsKey(cMode)) { 
   528  			hListModes.put(cMode, new ArrayList<ChannelListModeItem>());	
   529  		}
   530  		final ArrayList<ChannelListModeItem> lModes = hListModes.get(cMode);
   531  		for (int i = 0; i < lModes.size(); i++) {
   532  			if (myParser.getIRCStringConverter().equalsIgnoreCase(lModes.get(i).getItem(), newItem.getItem())) { 
   533  				if (bAdd) { return; }
   534  				else { 
   535  					lModes.remove(i);
   536  					break;
   537  				}
   538  			}
   539  		}
   540  		if (bAdd) { lModes.add(newItem); }
   541  	}
   542  	
   543  	/**
   544  	 * Get the list object representing a channel mode.
   545  	 *
   546  	 * @param cMode Character representing mode
   547  	 * @return ArrayList containing ChannelListModeItem in the list, or null if mode is invalid
   548  	 */
   549  	public List<ChannelListModeItem> getListModeParam(final Character cMode) {
        		 /* 
    P/P 		  *  Method: List getListModeParam(Character)
        		  * 
        		  *  Preconditions:
        		  *    this.myParser != null
        		  *    this.myParser.hChanModesOther != null
        		  *    (soft) this.hListModes != null
        		  * 
        		  *  Presumptions:
        		  *    java.util.Map:get(...)@550 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.Byte:byteValue(...)@550: {1}, {-128..0, 2..255}
        		  *    java.util.Map:containsKey(...)@550: {0}, {1}
        		  *    java.util.Map:containsKey(...)@552: {1}, {0}
        		  */
   550  		if (!myParser.hChanModesOther.containsKey(cMode) || myParser.hChanModesOther.get(cMode) != myParser.MODE_LIST) { return null; }
   551  		
   552  		if (!hListModes.containsKey(cMode)) { 
   553  			hListModes.put(cMode, new ArrayList<ChannelListModeItem>());
   554  		}
   555  		return hListModes.get(cMode);
   556  	}
   557  	
   558  	/**
   559  	 * Get the "adding state" of a list mode.
   560  	 * 
   561  	 * @param cMode Character representing mode 
   562  	 * @return false if we are not expecting a 367 etc, else true.
   563  	 */
   564  	public boolean getAddState(final Character cMode) { 
        		 /* 
    P/P 		  *  Method: bool getAddState(Character)
        		  * 
        		  *  Preconditions:
        		  *    this.lAddingModes != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(return_value)
        		  */
   565  		synchronized (lAddingModes) {
   566  			return lAddingModes.contains(cMode);
   567  		}
   568  	}
   569  	
   570  	/**
   571  	 * Get the "adding state" of a list mode.
   572  	 * 
   573  	 * @param cMode Character representing mode
   574  	 * @param newState change the value returned by getAddState
   575  	 */
   576  	protected void setAddState(final Character cMode, final boolean newState) { 
        		 /* 
    P/P 		  *  Method: void setAddState(Character, bool)
        		  * 
        		  *  Preconditions:
        		  *    this.lAddingModes != null
        		  * 
        		  *  Test Vectors:
        		  *    newState: {0}, {1}
        		  */
   577  		synchronized (lAddingModes) {
   578  			if (newState) {
   579  				lAddingModes.add(cMode);
   580  			} else {
   581  				if (lAddingModes.contains(cMode)) { lAddingModes.remove(cMode); }
   582  			}
   583  		}
   584  	}
   585  	
   586  	/**
   587  	 * Reset the "adding state" of *all* list modes.
   588  	 */
   589  	protected void resetAddState() {
        		 /* 
    P/P 		  *  Method: void resetAddState()
        		  * 
        		  *  Preconditions:
        		  *    this.lAddingModes != null
        		  */
   590  		synchronized (lAddingModes) {
   591  			lAddingModes.clear();
   592  		}
   593  	}
   594  	
   595  	/**
   596  	 * Adjust the channel modes on a channel.
   597  	 * This function will queue modes up to be sent in one go, according to 005 params.
   598  	 * If less modes are altered than the queue accepts, sendModes() must be called.<br><br>
   599  	 * sendModes is automatically called if you attempt to add more modes than is allowed
   600  	 * to be queued
   601  	 *
   602  	 * @param positive Is this a positive mode change, or a negative mode change
   603  	 * @param mode Character representing the mode to change
   604  	 * @param parameter Parameter needed to make change (not used if mode doesn't need a parameter)
   605  	 */
   606  	public void alterMode(final boolean positive, final Character mode, final String parameter) { 
        		 /* 
    P/P 		  *  Method: void alterMode(bool, Character, String)
        		  * 
        		  *  Preconditions:
        		  *    this.myParser != null
        		  *    this.myParser.h005Info != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.hParamModes != null
        		  *    (soft) this.lModeQueue != null
        		  *    (soft) this.myParser.cMyself != null
        		  *    (soft) init'ed(this.myParser.currentSocketState)
        		  *    (soft) this.myParser.hChanModesBool != null
        		  *    (soft) this.myParser.hChanModesOther != null
        		  *    (soft) this.myParser.hChannelList != null
        		  *    ...
        		  * 
        		  *  Presumptions:
        		  *    java.util.Map:get(...)@633 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.String:substring(...)._tainted)
        		  *    possibly_updated(java.lang.String:substring(...)._tainted)
        		  *    possibly_updated(this.myParser.cMyself.myAwayReason)
        		  *    this.myParser.stringConverter == One-of{old this.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1)}
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    new char[](IRCStringConverter#1) num objects <= 1
        		  *    init'ed(new char[](IRCStringConverter#1).length)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    positive: {1}, {0}
        		  *    java.lang.String:isEmpty(...)@643: {1}, {0}
        		  *    java.lang.String:matches(...)@1730: {1}, {0}
        		  *    java.util.List:contains(...)@622: {0}, {1}
        		  *    java.util.Map:containsKey(...)@610: {0}, {1}
        		  *    java.util.Map:containsKey(...)@620: {0}, {1}
        		  *    java.util.Map:containsKey(...)@630: {0}, {1}
        		  */
   607  		int modecount = 1;
   608  		int modeint = 0;
   609  		String modestr = "";
   610  		if (myParser.h005Info.containsKey("MODES")) {
   611  			try {
   612  				modecount = Integer.parseInt(myParser.h005Info.get("MODES")); 
   613  			} catch (NumberFormatException e) { 
   614  				modecount = 1; 
   615  			}
   616  		}
   617  		if (!myParser.isUserSettable(mode)) { return; }
   618  
   619  		modestr = ((positive) ? "+" : "-") + mode;
   620  		if (myParser.hChanModesBool.containsKey(mode)) {
   621  			final String teststr = ((positive) ? "-" : "+") + mode;
   622  			if (lModeQueue.contains(teststr)) {
   623  				lModeQueue.remove(teststr);
   624  				return;
   625  			} else if (lModeQueue.contains(modestr)) {
   626  				return;
   627  			}
   628  		} else {
   629  			// May need a param
   630  			if (myParser.hPrefixModes.containsKey(mode)) {
   631  				modestr = modestr + " " + parameter;
   632  			} else {
   633  				modeint = myParser.hChanModesOther.get(mode);
   634  				if ((modeint & IRCParser.MODE_LIST) == IRCParser.MODE_LIST) {
   635  					modestr = modestr + " " + parameter;
   636  				} else if (!positive && ((modeint & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET)) {
   637  					modestr = modestr + " " + parameter;
   638  				} else if (positive && ((modeint & IRCParser.MODE_SET) == IRCParser.MODE_SET)) {
   639  					// Does mode require a param to unset aswell?
   640  					// We might need to queue an unset first
   641  					if (((modeint & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET)) {
   642  						final String existingParam = getModeParam(mode);
   643  						if (!existingParam.isEmpty()) {
   644  							final String reverseModeStr = "-" + mode + " " + existingParam;
   645  							
   646  							myParser.callDebugInfo(IRCParser.DEBUG_INFO, "Queueing mode: %s", reverseModeStr);
   647  							lModeQueue.add(reverseModeStr);
   648  							if (lModeQueue.size() == modecount) { sendModes(); }
   649  						}
   650  					}
   651  					modestr = modestr + " " + parameter;
   652  				}
   653  			}
   654  		}
   655  		myParser.callDebugInfo(IRCParser.DEBUG_INFO, "Queueing mode: %s", modestr);
   656  		lModeQueue.add(modestr);
   657  		if (lModeQueue.size() == modecount) { sendModes(); }
   658  	}
   659  	
   660  	/**
   661  	 * This function will send modes that are currently queued up to send.
   662  	 * This assumes that the queue only contains the amount that are alowed to be sent
   663  	 * and thus will try to send the entire queue in one go.<br><br>
   664  	 * Modes are always sent negative then positive and not mixed.
   665  	 */
   666  	public void sendModes() { 
        		 /* 
    P/P 		  *  Method: void sendModes()
        		  * 
        		  *  Preconditions:
        		  *    this.lModeQueue != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.myParser != null
        		  *    (soft) this.myParser.cMyself != null
        		  *    (soft) init'ed(this.myParser.currentSocketState)
        		  *    (soft) this.myParser.hChanModesOther != null
        		  *    (soft) this.myParser.hChannelList != null
        		  *    (soft) this.myParser.myCallbackManager != null
        		  *    (soft) this.myParser.myCallbackManager.callbackHash != null
        		  *    (soft) init'ed(this.myParser.out)
        		  * 
        		  *  Presumptions:
        		  *    java.util.List:get(...)@677 != null
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.String:substring(...)._tainted)
        		  *    possibly_updated(this.myParser.cMyself.myAwayReason)
        		  *    this.myParser.stringConverter == One-of{old this.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1)}
        		  *    init'ed(this.myParser.stringConverter)
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    new char[](IRCStringConverter#1) num objects <= 1
        		  *    init'ed(new char[](IRCStringConverter#1).length)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:charAt(...)@679: {0..42, 44..216-1}, {43}
        		  *    java.lang.StringBuilder:length(...)@688: {-231..0}, {1..232-1}
        		  *    java.lang.StringBuilder:length(...)@689: {-231..0}, {1..232-1}
        		  *    java.lang.StringBuilder:length(...)@690: {-231..0}, {1..232-1}
        		  *    java.lang.StringBuilder:length(...)@691: {-231..0}, {1..232-1}
        		  *    java.util.List:isEmpty(...)@667: {0}, {1}
        		  */
   667  		if (lModeQueue.isEmpty()) { return; }
   668  		final StringBuilder positivemode = new StringBuilder();
   669  		final StringBuilder positiveparam = new StringBuilder();
   670  		final StringBuilder negativemode = new StringBuilder();
   671  		final StringBuilder negativeparam = new StringBuilder();
   672  		final StringBuilder sendModeStr = new StringBuilder();
   673  		String modestr;
   674  		String[] modeparam;
   675  		boolean positive;
   676  		for (int i = 0; i < lModeQueue.size(); ++i) {
   677  			modeparam = lModeQueue.get(i).split(" ");
   678  			modestr = modeparam[0];
   679  			positive = modestr.charAt(0) == '+';
   680  			if (positive) {
   681  				positivemode.append(modestr.charAt(1));
   682  				if (modeparam.length > 1) { positiveparam.append(" ").append(modeparam[1]); }
   683  			} else {
   684  				negativemode.append(modestr.charAt(1));
   685  				if (modeparam.length > 1) { negativeparam.append(" ").append(modeparam[1]); }
   686  			}
   687  		}
   688  		if (negativemode.length() > 0) { sendModeStr.append("-").append(negativemode); }
   689  		if (positivemode.length() > 0) { sendModeStr.append("+").append(positivemode); }
   690  		if (negativeparam.length() > 0) { sendModeStr.append(negativeparam); }
   691  		if (positiveparam.length() > 0) { sendModeStr.append(positiveparam); }
   692  		myParser.callDebugInfo(IRCParser.DEBUG_INFO, "Sending mode: %s", sendModeStr.toString());
   693  		myParser.sendLine("MODE " + sName + " " + sendModeStr.toString());
   694  		clearModeQueue();
   695  	}
   696  	
   697  	/**
   698  	 * This function will clear the mode queue (WITHOUT Sending).
   699  	 */
   700  	public void clearModeQueue() { 
        		 /* 
    P/P 		  *  Method: void clearModeQueue()
        		  * 
        		  *  Preconditions:
        		  *    this.lModeQueue != null
        		  */
   701  		lModeQueue.clear();
   702  	}
   703  	
   704  	/**
   705  	 * Send a private message to the channel.
   706  	 *
   707  	 * @param sMessage Message to send
   708  	 */
   709  	public void sendMessage(final String sMessage) { 
        		 /* 
    P/P 		  *  Method: void sendMessage(String)
        		  * 
        		  *  Preconditions:
        		  *    sMessage != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.myParser != null
        		  *    (soft) this.myParser.cMyself != null
        		  *    (soft) init'ed(this.myParser.currentSocketState)
        		  *    (soft) this.myParser.hChanModesOther != null
        		  *    (soft) this.myParser.hChannelList != null
        		  *    (soft) this.myParser.myCallbackManager != null
        		  *    (soft) this.myParser.myCallbackManager.callbackHash != null
        		  *    (soft) init'ed(this.myParser.out)
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.String:substring(...)._tainted)
        		  *    possibly_updated(this.myParser.cMyself.myAwayReason)
        		  *    this.myParser.stringConverter == One-of{old this.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1)}
        		  *    init'ed(this.myParser.stringConverter)
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    new char[](IRCStringConverter#1) num objects <= 1
        		  *    init'ed(new char[](IRCStringConverter#1).length)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:isEmpty(...)@710: {0}, {1}
        		  */
   710  		if (sMessage.isEmpty()) { return; }
   711  		
   712  		myParser.sendString("PRIVMSG " + sName + " :" + sMessage);	
   713  	}
   714  	
   715  	/**
   716  	 * Send a notice message to a target.
   717  	 *
   718  	 * @param sMessage Message to send
   719  	 */
   720  	public void sendNotice(final String sMessage) { 
        		 /* 
    P/P 		  *  Method: void sendNotice(String)
        		  * 
        		  *  Preconditions:
        		  *    sMessage != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.myParser != null
        		  *    (soft) this.myParser.cMyself != null
        		  *    (soft) init'ed(this.myParser.currentSocketState)
        		  *    (soft) this.myParser.hChanModesOther != null
        		  *    (soft) this.myParser.hChannelList != null
        		  *    (soft) this.myParser.myCallbackManager != null
        		  *    (soft) this.myParser.myCallbackManager.callbackHash != null
        		  *    (soft) init'ed(this.myParser.out)
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.String:substring(...)._tainted)
        		  *    possibly_updated(this.myParser.cMyself.myAwayReason)
        		  *    this.myParser.stringConverter == One-of{old this.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1)}
        		  *    init'ed(this.myParser.stringConverter)
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    new char[](IRCStringConverter#1) num objects <= 1
        		  *    init'ed(new char[](IRCStringConverter#1).length)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:isEmpty(...)@721: {0}, {1}
        		  */
   721  		if (sMessage.isEmpty()) { return; }
   722  		
   723  		myParser.sendString("NOTICE " + sName + " :" + sMessage);	
   724  	}
   725  
   726  	/**
   727  	 * Send a private message to a target.
   728  	 *
   729  	 * @param sMessage Message to send
   730  	 */
   731  	public void sendAction(final String sMessage) { 
        		 /* 
    P/P 		  *  Method: void sendAction(String)
        		  * 
        		  *  Preconditions:
        		  *    sMessage != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) this.myParser != null
        		  *    (soft) this.myParser.cMyself != null
        		  *    (soft) init'ed(this.myParser.currentSocketState)
        		  *    (soft) this.myParser.hChanModesOther != null
        		  *    (soft) this.myParser.hChannelList != null
        		  *    (soft) this.myParser.myCallbackManager != null
        		  *    (soft) this.myParser.myCallbackManager.callbackHash != null
        		  *    (soft) init'ed(this.myParser.out)
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.String:substring(...)._tainted)
        		  *    possibly_updated(this.myParser.cMyself.myAwayReason)
        		  *    this.myParser.stringConverter == One-of{old this.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1)}
        		  *    init'ed(this.myParser.stringConverter)
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    new char[](IRCStringConverter#1) num objects <= 1
        		  *    init'ed(new char[](IRCStringConverter#1).length)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:isEmpty(...)@732: {0}, {1}
        		  */
   732  		if (sMessage.isEmpty()) { return; }
   733  		sendCTCP("ACTION", sMessage);
   734  	}
   735  	
   736  	/**
   737  	 * Send a CTCP to a target.
   738  	 *
   739  	 * @param sType Type of CTCP
   740  	 * @param sMessage Optional Additional Parameters
   741  	 */
   742  	public void sendCTCP(final String sType, String sMessage) { 
        		 /* 
    P/P 		  *  Method: void sendCTCP(String, String)
        		  * 
        		  *  Preconditions:
        		  *    sType != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) sMessage != null
        		  *    (soft) this.myParser != null
        		  *    (soft) this.myParser.cMyself != null
        		  *    (soft) init'ed(this.myParser.currentSocketState)
        		  *    (soft) this.myParser.hChanModesOther != null
        		  *    (soft) this.myParser.hChannelList != null
        		  *    (soft) this.myParser.myCallbackManager != null
        		  *    (soft) this.myParser.myCallbackManager.callbackHash != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.String:substring(...)._tainted)
        		  *    possibly_updated(this.myParser.cMyself.myAwayReason)
        		  *    this.myParser.stringConverter == One-of{old this.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1)}
        		  *    init'ed(this.myParser.stringConverter)
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    new char[](IRCStringConverter#1) num objects <= 1
        		  *    init'ed(new char[](IRCStringConverter#1).length)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:isEmpty(...)@743: {0}, {1}
        		  *    java.lang.String:isEmpty(...)@745: {1}, {0}
        		  */
   743  		if (sType.isEmpty()) { return; }
   744  		final char char1 = (char) 1;
   745  		if (!sMessage.isEmpty()) { sMessage = " " + sMessage; }
   746  		sendMessage(char1 + sType.toUpperCase() + sMessage + char1);
   747  	}
   748  	
   749  	/**
   750  	 * Send a CTCPReply to a target.
   751  	 *
   752  	 * @param sType Type of CTCP
   753  	 * @param sMessage Optional Additional Parameters
   754  	 */
   755  	public void sendCTCPReply(final String sType, String sMessage) { 
        		 /* 
    P/P 		  *  Method: void sendCTCPReply(String, String)
        		  * 
        		  *  Preconditions:
        		  *    sType != null
        		  *    (soft) init'ed(this.myParser.stringConverter)
        		  *    (soft) sMessage != null
        		  *    (soft) this.myParser != null
        		  *    (soft) this.myParser.cMyself != null
        		  *    (soft) init'ed(this.myParser.currentSocketState)
        		  *    (soft) this.myParser.hChanModesOther != null
        		  *    (soft) this.myParser.hChannelList != null
        		  *    (soft) this.myParser.myCallbackManager != null
        		  *    (soft) this.myParser.myCallbackManager.callbackHash != null
        		  *    ...
        		  * 
        		  *  Postconditions:
        		  *    init'ed(java.lang.String:substring(...)._tainted)
        		  *    possibly_updated(this.myParser.cMyself.myAwayReason)
        		  *    this.myParser.stringConverter == One-of{old this.myParser.stringConverter, &amp;new IRCStringConverter(getIRCStringConverter#1)}
        		  *    init'ed(this.myParser.stringConverter)
        		  *    new IRCStringConverter(getIRCStringConverter#1) num objects <= 1
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).limit)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).lowercase)
        		  *    init'ed(new IRCStringConverter(getIRCStringConverter#1).uppercase)
        		  *    new char[](IRCStringConverter#1) num objects <= 1
        		  *    init'ed(new char[](IRCStringConverter#1).length)
        		  *    ...
        		  * 
        		  *  Test Vectors:
        		  *    java.lang.String:isEmpty(...)@756: {0}, {1}
        		  *    java.lang.String:isEmpty(...)@758: {1}, {0}
        		  */
   756  		if (sType.isEmpty()) { return; }
   757  		final char char1 = (char) 1;
   758  		if (!sMessage.isEmpty()) { sMessage = " " + sMessage; }
   759  		sendNotice(char1 + sType.toUpperCase() + sMessage + char1);	
   760  	}
   761  	
   762  	/**
   763  	 * Get a string representation of the Channel.
   764  	 *
   765  	 * @return String representation of the Channel.
   766  	 */
   767  	@Override
        	 /* 
    P/P 	  *  Method: String toString()
        	  * 
        	  *  Postconditions:
        	  *    return_value == this.sName
        	  *    init'ed(return_value)
        	  */
   768  	public String toString() { return sName; }
   769  	
   770  	/**
   771  	 * Get the parser object that owns this channel.
   772  	 *
   773  	 * @return The parser object that owns this channel
   774  	 */
        	 /* 
    P/P 	  *  Method: IRCParser getParser()
        	  * 
        	  *  Postconditions:
        	  *    return_value == this.myParser
        	  *    init'ed(return_value)
        	  */
   775  	public IRCParser getParser() { return myParser; }
   776  
   777  }
   778  








SofCheck Inspector Build Version : 2.17854
ChannelInfo.java 2009-Jun-25 01:54:24
ChannelInfo.class 2009-Sep-02 17:04:11