File Source: Server.java

         /* 
    P/P   *  Method: com.dmdirc.Server$1__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;
    24  
    25  import com.dmdirc.actions.ActionManager;
    26  import com.dmdirc.actions.CoreActionType;
    27  import com.dmdirc.actions.wrappers.AliasWrapper;
    28  import com.dmdirc.commandparser.CommandManager;
    29  import com.dmdirc.commandparser.CommandType;
    30  import com.dmdirc.config.ConfigManager;
    31  import com.dmdirc.config.Identity;
    32  import com.dmdirc.config.IdentityManager;
    33  import com.dmdirc.interfaces.AwayStateListener;
    34  import com.dmdirc.interfaces.InviteListener;
    35  import com.dmdirc.logger.ErrorLevel;
    36  import com.dmdirc.logger.Logger;
    37  import com.dmdirc.parser.irc.ChannelInfo;
    38  import com.dmdirc.parser.irc.ClientInfo;
    39  import com.dmdirc.parser.irc.IRCParser;
    40  import com.dmdirc.parser.irc.IRCStringConverter;
    41  import com.dmdirc.parser.irc.MyInfo;
    42  import com.dmdirc.parser.irc.ParserError;
    43  import com.dmdirc.parser.irc.ServerInfo;
    44  import com.dmdirc.ui.WindowManager;
    45  import com.dmdirc.ui.input.TabCompleter;
    46  import com.dmdirc.ui.input.TabCompletionType;
    47  import com.dmdirc.ui.interfaces.InputWindow;
    48  import com.dmdirc.ui.interfaces.ServerWindow;
    49  import com.dmdirc.ui.interfaces.Window;
    50  
    51  import java.io.Serializable;
    52  import java.util.ArrayList;
    53  import java.util.Hashtable;
    54  import java.util.List;
    55  import java.util.Map;
    56  import java.util.Timer;
    57  import java.util.TimerTask;
    58  
    59  import javax.net.ssl.TrustManager;
    60  
    61  /**
    62   * The Server class represents the client's view of a server. It maintains
    63   * a list of all channels, queries, etc, and handles parser callbacks pertaining
    64   * to the server.
    65   *
    66   * @author chris
    67   */
         /* 
    P/P   *  Method: com.dmdirc.Server__static_init
          * 
          *  Postconditions:
          *    DOMAIN_GENERAL != null
          *    DOMAIN_PROFILE != null
          *    DOMAIN_SERVER != null
          */
    68  public class Server extends WritableFrameContainer implements Serializable {
    69  
    70      // <editor-fold defaultstate="collapsed" desc="Properties">
    71  
    72      // <editor-fold defaultstate="collapsed" desc="Static">
    73  
    74      /**
    75       * A version number for this class. It should be changed whenever the class
    76       * structure is changed (or anything else that would prevent serialized
    77       * objects being unserialized with the new class).
    78       */
    79      private static final long serialVersionUID = 1;
    80  
    81      /** The name of the general domain. */
    82      private static final String DOMAIN_GENERAL = "general".intern();
    83      /** The name of the profile domain. */
    84      private static final String DOMAIN_PROFILE = "profile".intern();
    85      /** The name of the server domain. */
    86      private static final String DOMAIN_SERVER = "server".intern();
    87  
    88      // </editor-fold>
    89  
    90      // <editor-fold defaultstate="collapsed" desc="Instance">
    91  
    92      /** Open channels that currently exist on the server. */
    93      private final Map<String, Channel> channels  = new Hashtable<String, Channel>();
    94      /** Open query windows on the server. */
    95      private final List<Query> queries = new ArrayList<Query>();
    96  
    97      /** The IRC Parser instance handling this server. */
    98      private transient IRCParser parser;
    99      /** The IRC Parser Thread. */
   100      private transient Thread parserThread;
   101      /** The raw frame used for this server instance. */
   102      private Raw raw;
   103      /** The ServerWindow corresponding to this server. */
   104      private ServerWindow window;
   105  
   106      /** The details of the server we're connecting to. */
   107      private ServerInfo serverInfo;
   108  
   109      /** The profile we're using. */
   110      private transient Identity profile;
   111  
   112      /** The current state of this server. */
   113      private final ServerStatus myState = new ServerStatus();
   114  
   115      /** The timer we're using to delay reconnects. */
   116      private Timer reconnectTimer;
   117  
   118      /** Channels we're meant to auto-join. */
   119      private final List<String> autochannels;
   120  
   121      /** The tabcompleter used for this server. */
   122      private final TabCompleter tabCompleter = new TabCompleter();
   123      /** The last activated internal frame for this server. */
   124      private FrameContainer activeFrame = this;
   125  
   126      /** Our reason for being away, if any. */
   127      private String awayMessage;
   128  
   129      /** Our event handler. */
   130      private final ServerEventHandler eventHandler = new ServerEventHandler(this);
   131  
   132      /** A list of outstanding invites. */
   133      private final List<Invite> invites = new ArrayList<Invite>();
   134  
   135      /** Our ignore list. */
   136      private final IgnoreList ignoreList = new IgnoreList();
   137  
   138      /** Our string convertor. */
   139      private IRCStringConverter converter = new IRCStringConverter();
   140  
   141      /** The parser factory to use. */
   142      private final ParserFactory parserFactory;
   143  
   144      // </editor-fold>
   145      
   146      // </editor-fold>
   147  
   148      // <editor-fold defaultstate="collapsed" desc="Constructors">
   149  
   150      /**
   151       * Creates a new instance of Server. Does not auto-join any channels, and
   152       * uses a default {@link ParserFactory}.
   153       *
   154       * @param server The hostname/ip of the server to connect to
   155       * @param port The port to connect to
   156       * @param password The server password
   157       * @param ssl Whether to use SSL or not
   158       * @param profile The profile to use
   159       */
   160      public Server(final String server, final int port, final String password,
   161              final boolean ssl, final Identity profile) {
                 /* 
    P/P           *  Method: void com.dmdirc.Server(String, int, String, bool, Identity)
                  * 
                  *  Preconditions:
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  *    (soft) init'ed(com/dmdirc/actions/wrappers/AliasWrapper.me)
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    com/dmdirc/actions/wrappers/AliasWrapper.me == old com/dmdirc/actions/wrappers/AliasWrapper.me
                  *    init'ed(this.activeFrame)
                  *    this.autochannels == undefined
                  *    this.autochannels == null
                  *    this.changer == this.autochannels
                  *    this.channels == this.autochannels
                  *    this.config == this.autochannels
                  *    this.eventHandler == this.autochannels
                  *    this.ignoreList == this.autochannels
                  *    ...
                  */
   162          this(server, port, password, ssl, profile, new ArrayList<String>());
   163      }
   164  
   165      /**
   166       * Creates a new instance of Server. Uses a default {@link ParserFactory}.
   167       *
   168       * @param server The hostname/ip of the server to connect to
   169       * @param port The port to connect to
   170       * @param password The server password
   171       * @param ssl Whether to use SSL or not
   172       * @param profile The profile to use
   173       * @param autochannels A list of channels to auto-join when we connect
   174       */
   175      public Server(final String server, final int port, final String password,
   176              final boolean ssl, final Identity profile, final List<String> autochannels) {
                 /* 
    P/P           *  Method: void com.dmdirc.Server(String, int, String, bool, Identity, List)
                  * 
                  *  Preconditions:
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  *    (soft) init'ed(com/dmdirc/actions/wrappers/AliasWrapper.me)
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    com/dmdirc/actions/wrappers/AliasWrapper.me == old com/dmdirc/actions/wrappers/AliasWrapper.me
                  *    init'ed(this.activeFrame)
                  *    this.autochannels == undefined
                  *    this.autochannels == null
                  *    this.changer == this.autochannels
                  *    this.channels == this.autochannels
                  *    this.config == this.autochannels
                  *    this.eventHandler == this.autochannels
                  *    this.ignoreList == this.autochannels
                  *    ...
                  */
   177          this(server, port, password, ssl, profile, autochannels, new ParserFactory());
   178      }
   179  
   180      /**
   181       * Creates a new instance of Server.
   182       *
   183       * @since 0.6
   184       * @param server The hostname/ip of the server to connect to
   185       * @param port The port to connect to
   186       * @param password The server password
   187       * @param ssl Whether to use SSL or not
   188       * @param profile The profile to use
   189       * @param autochannels A list of channels to auto-join when we connect
   190       * @param factory The {@link ParserFactory} to use to create parsers
   191       */
   192      public Server(final String server, final int port, final String password,
   193              final boolean ssl, final Identity profile,
   194              final List<String> autochannels, final ParserFactory factory) {
                 /* 
    P/P           *  Method: void com.dmdirc.Server(String, int, String, bool, Identity, List, ParserFactory)
                  * 
                  *  Preconditions:
                  *    com/dmdirc/Main.controller != null
                  *    init'ed(com/dmdirc/ServerManager.me)
                  *    init'ed(com/dmdirc/actions/wrappers/AliasWrapper.me)
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.commandparser.CommandType.TYPE_GLOBAL)
                  *    init'ed(com.dmdirc.commandparser.CommandType.TYPE_SERVER)
                  *    init'ed(com.dmdirc.ui.input.TabCompletionType.COMMAND)
                  *    com.dmdirc.ui.interfaces.ServerWindow:getInputHandler(...)@209 != null
                  *    com.dmdirc.ui.interfaces.UIController:getServer(...)@200 != null
                  *    ...
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == One-of{old com/dmdirc/ServerManager.me, &amp;new ServerManager(getServerManager#1)}
                  *    com/dmdirc/ServerManager.me != null
                  *    com/dmdirc/actions/wrappers/AliasWrapper.me == One-of{old com/dmdirc/actions/wrappers/AliasWrapper.me, &amp;new AliasWrapper(getAliasWrapper#1)}
                  *    com/dmdirc/actions/wrappers/AliasWrapper.me != null
                  *    this.activeFrame == this
                  *    this.activeFrame != null
                  *    this.eventHandler.owner == this.activeFrame
                  *    this.autochannels == autochannels
                  *    init'ed(this.autochannels)
                  *    this.changer == &amp;new FrameContainer$IconChanger(FrameContainer#2)
                  *    ...
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@232: {0}, {1}
                  */
   195          super("server-disconnected", new ConfigManager("", "", server));
   196  
   197          serverInfo = new ServerInfo(server, port, password);
   198          serverInfo.setSSL(ssl);
   199  
   200          window = Main.getUI().getServer(this);
   201  
   202          ServerManager.getServerManager().registerServer(this);
   203          WindowManager.addWindow(window);
   204  
   205          window.setTitle(server + ":" + port);
   206  
   207          tabCompleter.addEntries(TabCompletionType.COMMAND,
   208                  AliasWrapper.getAliasWrapper().getAliases());
   209          window.getInputHandler().setTabCompleter(tabCompleter);
   210  
   211          updateIcon();
   212  
   213          window.open();
   214  
   215          tabCompleter.addEntries(TabCompletionType.COMMAND,
   216                  CommandManager.getCommandNames(CommandType.TYPE_SERVER));
   217          tabCompleter.addEntries(TabCompletionType.COMMAND,
   218                  CommandManager.getCommandNames(CommandType.TYPE_GLOBAL));
   219  
   220          this.autochannels = autochannels;
   221          this.parserFactory = factory;
   222  
                 /* 
    P/P           *  Method: void com.dmdirc.Server$1(Server)
                  */
   223          new Timer("Server Who Timer").schedule(new TimerTask() {
   224              @Override
   225              public void run() {
                         /* 
    P/P                   *  Method: void run()
                          * 
                          *  Preconditions:
                          *    this.channels != null
                          * 
                          *  Presumptions:
                          *    channel.channelInfo@226 != null
                          *    channel.server.parser@226 != null
                          *    channel.server@226 != null
                          *    java.util.Iterator:next(...)@226 != null
                          *    java.util.Map:values(...)@226 != null
                          * 
                          *  Test Vectors:
                          *    java.util.Iterator:hasNext(...)@226: {0}, {1}
                          */
   226                  for (Channel channel : channels.values()) {
   227                      channel.checkWho();
   228                  }
   229              }
   230          }, 0, getConfigManager().getOptionInt(DOMAIN_GENERAL, "whotime"));
   231  
   232          if (getConfigManager().getOptionBool(DOMAIN_GENERAL, "showrawwindow")) {
   233              addRaw();
   234          }
   235  
   236          connect(server, port, password, ssl, profile);
   237      }
   238  
   239      // </editor-fold>
   240  
   241      // <editor-fold defaultstate="collapsed" desc="Connection, disconnection & reconnection">
   242  
   243      /**
   244       * Connects to a new server with the specified details.
   245       *
   246       * @param server The hostname/ip of the server to connect to
   247       * @param port The port to connect to
   248       * @param password The server password
   249       * @param ssl Whether to use SSL or not
   250       * @param profile The profile to use
   251       */
   252      @Precondition({
   253          "The IRC Parser is null or not connected",
   254          "The specified profile is not null"
   255      })
   256      @SuppressWarnings("fallthrough")
   257      public void connect(final String server, final int port, final String password,
   258              final boolean ssl, final Identity profile) {
                 /* 
    P/P           *  Method: void connect(String, int, String, bool, Identity)
                  * 
                  *  Preconditions:
                  *    this.myState.state != null
                  *    profile != null
                  *    this.myState != null
                  *    (soft) init'ed(com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...])
                  *    (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#17).type != null
                  *    (soft) init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    (soft) this.parser == null
                  *    (soft) this.parserThread != null
                  *    (soft) this.serverInfo != null
                  *    (soft) this...parser != null
                  *    ...
                  * 
                  *  Presumptions:
                  *    com.dmdirc.ServerState:ordinal(...)@262 in {0..6}
                  *    init'ed(com.dmdirc.logger.ErrorLevel.FATAL)
                  *    java.util.Arrays:asList(...)@89 != null
                  *    this...parser@275 != null
                  *    this.eventHandler.owner.server@275 != null
                  *    ...
                  * 
                  *  Postconditions:
                  *    possibly_updated(com/dmdirc/ServerManager.me)
                  *    possibly_updated(this.awayMessage)
                  *    possibly_updated(this.icon)
                  *    this.myState.state not in Addr_Set{null,&amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#5),&amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#7)}
                  *    this.parser in Addr_Set{null,&amp;new IRCParser(getParser#1)}
                  *    possibly_updated(this.parserThread)
                  *    possibly_updated(this.profile)
                  *    init'ed(this.serverInfo)
                  *    new ArrayList(ServerManager#1) num objects == 0, if init'ed
                  *    new IRCParser(getParser#1) num objects <= 1
                  *    ...
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...]: {1}, {2}, {3,4}, {5}, {-231..0, 6..232-1}
                  */
   259          assert profile != null;
   260  
   261          synchronized (myState) {
                     /* 
    P/P               *  Method: com.dmdirc.Server$4__static_init
                      * 
                      *  Preconditions:
                      *    (soft) init'ed(com.dmdirc.ServerState__static_init.new ServerState[](ServerState__static_init#15)[...])
                      * 
                      *  Presumptions:
                      *    com.dmdirc.ServerState:ordinal(...)@262 in {0..6}
                      * 
                      *  Postconditions:
                      *    new int[](Server$4__static_init#1) num objects == 1
                      */
   262              switch (myState.getState()) {
   263                  case RECONNECT_WAIT:
   264                      reconnectTimer.cancel();
   265                      break;
   266                  case CLOSING:
   267                      // Ignore the connection attempt
   268                      return;
   269                  case CONNECTED:
   270                  case CONNECTING:
   271                      disconnect(getConfigManager().getOption(DOMAIN_GENERAL, "quitmessage"));
   272                  case DISCONNECTING:
   273                      while (!myState.getState().isDisconnected()) {
   274                          try {
   275                              myState.wait();
   276                          } catch (InterruptedException ex) {
   277                              return;
   278                          }
   279                      }
   280                      break;
   281                  default:
   282                      // Do nothing
   283                      break;
   284              }
   285  
   286              if (parser != null) {
   287                  throw new IllegalArgumentException("Connection attempt while parser "
   288                          + "is still connected.\n\nMy state:" + getState());
   289              }
   290  
   291              myState.transition(ServerState.CONNECTING);
   292  
   293              ActionManager.processEvent(CoreActionType.SERVER_CONNECTING, null, this);
   294  
   295              getConfigManager().migrate("", "", server);
   296  
   297              serverInfo = buildServerInfo(server, port, password, ssl);
   298  
   299              this.profile = profile;
   300  
   301              updateIcon();
   302  
   303              addLine("serverConnecting", server, port);
   304  
   305              parser = buildParser();
   306  
   307              doCallbacks();
   308  
   309              awayMessage = null;
   310              removeInvites();
   311              window.setAwayIndicator(false);
   312  
   313              try {
   314                  parserThread = new Thread(parser, "IRC Parser thread");
   315                  parserThread.start();
   316              } catch (IllegalThreadStateException ex) {
   317                  Logger.appError(ErrorLevel.FATAL, "Unable to start IRC Parser", ex);
   318              }
   319          }
   320      }
   321  
   322      /**
   323       * Reconnects to the IRC server with a specified reason.
   324       *
   325       * @param reason The quit reason to send
   326       */
   327      public void reconnect(final String reason) {
                 /* 
    P/P           *  Method: void reconnect(String)
                  * 
                  *  Preconditions:
                  *    this.myState.state != null
                  *    this.myState != null
                  *    (soft) init'ed(com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...])
                  *    (soft) init'ed(this.parser)
                  *    (soft) this.parserThread != null
                  *    (soft) init'ed(this.profile)
                  *    (soft) this.serverInfo != null
                  *    (soft) this.channels != null
                  *    (soft) this.config != null
                  *    (soft) this.invites != null
                  *    ...
                  * 
                  *  Presumptions:
                  *    java.util.Arrays:asList(...)@89 init'ed
                  * 
                  *  Postconditions:
                  *    this.awayMessage == old this.awayMessage
                  *    this.icon == One-of{old this.icon, &amp;"secure-server", &amp;"server", &amp;"server-disconnected"}
                  *    this.myState.state == One-of{old this.myState.state, &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#7), &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#9)}
                  *    this.myState.state != null
                  *    this.parser == old this.parser
                  *    init'ed(this.parser)
                  *    this.parserThread == old this.parserThread
                  *    this.parserThread != null
                  *    this.profile == old this.profile
                  *    init'ed(this.profile)
                  *    ...
                  */
   328          synchronized (myState) {
   329              if (myState.getState() == ServerState.CLOSING) {
   330                  return;
   331              }
   332  
   333              disconnect(reason);
   334              
   335              connect(serverInfo.getHost(), serverInfo.getPort(),
   336                      serverInfo.getPassword(), serverInfo.getSSL(), profile);
   337          }
   338      }
   339  
   340      /**
   341       * Reconnects to the IRC server.
   342       */
   343      public void reconnect() {
                 /* 
    P/P           *  Method: void reconnect()
                  * 
                  *  Preconditions:
                  *    this.myState.state != null
                  *    this.config != null
                  *    this.myState != null
                  *    (soft) init'ed(com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...])
                  *    (soft) init'ed(this.parser)
                  *    (soft) this.parserThread != null
                  *    (soft) init'ed(this.profile)
                  *    (soft) this.serverInfo != null
                  *    (soft) this.channels != null
                  *    (soft) this.invites != null
                  *    ...
                  * 
                  *  Postconditions:
                  *    this.awayMessage == old this.awayMessage
                  *    this.icon == One-of{old this.icon, &amp;"secure-server", &amp;"server", &amp;"server-disconnected"}
                  *    this.myState.state == One-of{old this.myState.state, &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#7), &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#9)}
                  *    this.myState.state != null
                  *    this.parser == old this.parser
                  *    init'ed(this.parser)
                  *    this.parserThread == old this.parserThread
                  *    this.parserThread != null
                  *    this.profile == old this.profile
                  *    init'ed(this.profile)
                  *    ...
                  */
   344          reconnect(getConfigManager().getOption(DOMAIN_GENERAL, "reconnectmessage"));
   345      }
   346  
   347      /**
   348       * Disconnects from the server with the default quit message.
   349       */
   350      public void disconnect() {
                 /* 
    P/P           *  Method: void disconnect()
                  * 
                  *  Preconditions:
                  *    this.myState.state != null
                  *    this.config != null
                  *    this.myState != null
                  *    (soft) init'ed(com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...])
                  *    (soft) this.channels != null
                  *    (soft) this.invites != null
                  *    (soft) this.listeners != null
                  *    (soft) this.myState.history != null
                  *    (soft) this.myState.state.transitions != null
                  *    (soft) init'ed(this.parser)
                  *    ...
                  * 
                  *  Postconditions:
                  *    this.icon == One-of{old this.icon, &amp;"secure-server", &amp;"server", &amp;"server-disconnected"}
                  *    this.myState.state == One-of{old this.myState.state, &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#7), &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#9)}
                  *    this.myState.state != null
                  */
   351          disconnect(getConfigManager().getOption(DOMAIN_GENERAL, "quitmessage"));
   352      }
   353  
   354      /**
   355       * Disconnects from the server.
   356       *
   357       * @param reason disconnect reason
   358       */
   359      public void disconnect(final String reason) {
                 /* 
    P/P           *  Method: void disconnect(String)
                  * 
                  *  Preconditions:
                  *    this.myState.state != null
                  *    this.myState != null
                  *    (soft) init'ed(com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...])
                  *    (soft) this.channels != null
                  *    (soft) this.config != null
                  *    (soft) this.invites != null
                  *    (soft) this.listeners != null
                  *    (soft) this.myState.history != null
                  *    (soft) this.myState.state.transitions != null
                  *    (soft) init'ed(this.parser)
                  *    ...
                  * 
                  *  Presumptions:
                  *    com.dmdirc.ServerState:ordinal(...)@361 in {0..6}
                  * 
                  *  Postconditions:
                  *    this.icon == One-of{old this.icon, &amp;"secure-server", &amp;"server", &amp;"server-disconnected"}
                  *    this.myState.state == One-of{old this.myState.state, &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#7), &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#9)}
                  *    this.myState.state != null
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...]: {1}, {2, 5..7}, {-231..0, 3,4, 8..232-1}
                  *    this.parser: Inverse{null}, Addr_Set{null}
                  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@388: {0}, {1}
                  */
   360          synchronized (myState) {
   361              switch (myState.getState()) {
   362              case CLOSING:
   363              case DISCONNECTING:
   364              case DISCONNECTED:
   365              case TRANSIENTLY_DISCONNECTED:
   366                  return;
   367              case RECONNECT_WAIT:
   368                  reconnectTimer.cancel();
   369                  break;
   370              default:
   371                  break;
   372              }
   373  
   374              clearChannels();
   375  
   376              if (parser == null) {
   377                  myState.transition(ServerState.DISCONNECTED);
   378              } else {
   379                  myState.transition(ServerState.DISCONNECTING);
   380  
   381                  removeInvites();
   382                  updateIcon();
   383  
   384                  parserThread.interrupt();
   385                  parser.disconnect(reason);
   386              }
   387  
   388              if (getConfigManager().getOptionBool(DOMAIN_GENERAL,
   389                      "closechannelsonquit")) {
   390                  closeChannels();
   391              }
   392  
   393              if (getConfigManager().getOptionBool(DOMAIN_GENERAL,
   394                      "closequeriesonquit")) {
   395                  closeQueries();
   396              }
   397          }
   398      }
   399  
   400      /**
   401       * Schedules a reconnect attempt to be performed after a user-defiend delay.
   402       */
   403      @Precondition("The server state is transiently disconnected")
   404      private void doDelayedReconnect() {
                 /* 
    P/P           *  Method: void doDelayedReconnect()
                  * 
                  *  Preconditions:
                  *    this.myState.state != null
                  *    this.config != null
                  *    this.listeners != null
                  *    this.myState != null
                  *    this.myState.history != null
                  *    this.myState.state.transitions != null
                  *    this.serverInfo != null
                  *    (soft) com/dmdirc/Main.controller != null
                  * 
                  *  Postconditions:
                  *    this.icon == &amp;"server-disconnected"
                  *    this.myState.state == &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#11)
                  *    this.reconnectTimer == &amp;new Timer(doDelayedReconnect#4)
                  *    new Timer(doDelayedReconnect#4) num objects == 1
                  */
   405          synchronized (myState) {
   406              if (myState.getState() != ServerState.TRANSIENTLY_DISCONNECTED) {
   407                  throw new IllegalStateException("doDelayedReconnect when not "
   408                          + "transiently disconnected\n\nState: " + myState);
   409              }
   410  
   411              final int delay = Math.max(1000,
   412                      getConfigManager().getOptionInt(DOMAIN_GENERAL, "reconnectdelay"));
   413  
   414              handleNotification("connectRetry", getName(), delay / 1000);
   415  
   416              reconnectTimer = new Timer("Server Reconnect Timer");
                     /* 
    P/P               *  Method: void com.dmdirc.Server$2(Server)
                      */
   417              reconnectTimer.schedule(new TimerTask() {
   418                  @Override
   419                  public void run() {
                             /* 
    P/P                       *  Method: void run()
                              * 
                              *  Preconditions:
                              *    init'ed(this.myState.state)
                              *    this.myState != null
                              *    (soft) init'ed(com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...])
                              *    (soft) init'ed(this.parser)
                              *    (soft) this.parserThread != null
                              *    (soft) init'ed(this.profile)
                              *    (soft) this.serverInfo != null
                              *    (soft) this.channels != null
                              *    (soft) this.config != null
                              *    (soft) this.invites != null
                              *    ...
                              * 
                              *  Presumptions:
                              *    java.util.Arrays:asList(...)@89 != null
                              * 
                              *  Postconditions:
                              *    this.awayMessage == old this.awayMessage
                              *    this.icon == One-of{old this.icon, &amp;"server-disconnected"}
                              *    init'ed(this.myState.state)
                              *    this.parser == old this.parser
                              *    init'ed(this.parser)
                              *    this.parserThread == old this.parserThread
                              *    this.profile == old this.profile
                              *    init'ed(this.profile)
                              *    this.serverInfo == old this.serverInfo
                              */
   420                      synchronized (myState) {
   421                          if (myState.getState() == ServerState.RECONNECT_WAIT) {
   422                              myState.transition(ServerState.TRANSIENTLY_DISCONNECTED);
   423                              reconnect();
   424                          }
   425                      }
   426                  }
   427              }, delay);
   428  
   429              myState.transition(ServerState.RECONNECT_WAIT);
   430              updateIcon();
   431          }
   432      }
   433  
   434      // </editor-fold>
   435  
   436      // <editor-fold defaultstate="collapsed" desc="Child windows">
   437  
   438      /**
   439       * Determines whether the server knows of the specified channel.
   440       *
   441       * @param channel The channel to be checked
   442       * @return True iff the channel is known, false otherwise
   443       */
   444      public boolean hasChannel(final String channel) {
                 /* 
    P/P           *  Method: bool hasChannel(String)
                  * 
                  *  Preconditions:
                  *    this.channels != null
                  *    this.converter != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   445          return channels.containsKey(converter.toLowerCase(channel));
   446      }
   447  
   448      /**
   449       * Retrieves the specified channel belonging to this server.
   450       *
   451       * @param channel The channel to be retrieved
   452       * @return The appropriate channel object
   453       */
   454      public Channel getChannel(final String channel) {
                 /* 
    P/P           *  Method: Channel getChannel(String)
                  * 
                  *  Preconditions:
                  *    this.channels != null
                  *    this.converter != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   455          return channels.get(converter.toLowerCase(channel));
   456      }
   457  
   458      /**
   459       * Retrieves a list of channel names belonging to this server.
   460       *
   461       * @return list of channel names belonging to this server
   462       */
   463      public List<String> getChannels() {
                 /* 
    P/P           *  Method: List getChannels()
                  * 
                  *  Preconditions:
                  *    this.channels != null
                  * 
                  *  Presumptions:
                  *    java.util.Map:keySet(...)@466 != null
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new ArrayList(getChannels#1)
                  *    new ArrayList(getChannels#1) num objects == 1
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@466: {0}, {1}
                  */
   464          final ArrayList<String> res = new ArrayList<String>();
   465  
   466          for (String channel : channels.keySet()) {
   467              res.add(channel);
   468          }
   469  
   470          return res;
   471      }
   472  
   473      /**
   474       * Determines whether the server knows of the specified query.
   475       *
   476       * @param host The host of the query to look for
   477       * @return True iff the query is known, false otherwise
   478       */
   479      public boolean hasQuery(final String host) {
                 /* 
    P/P           *  Method: bool hasQuery(String)
                  * 
                  *  Preconditions:
                  *    this.queries != null
                  *    (soft) this.converter != null
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:next(...)@482 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.parser.irc.IRCStringConverter:equalsIgnoreCase(...)@483: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@482: {0}, {1}
                  */
   480          final String nick = ClientInfo.parseHost(host);
   481  
   482          for (Query query : queries) {
   483              if (converter.equalsIgnoreCase(ClientInfo.parseHost(query.getHost()), nick)) {
   484                  return true;
   485              }
   486          }
   487  
   488          return false;
   489      }
   490  
   491      /**
   492       * Retrieves the specified query belonging to this server.
   493       *
   494       * @param host The host of the query to look for
   495       * @return The appropriate query object
   496       */
   497      public Query getQuery(final String host) {
                 /* 
    P/P           *  Method: Query getQuery(String)
                  * 
                  *  Preconditions:
                  *    this.converter != null
                  *    this.queries != null
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:hasNext(...)@500 == 1
                  *    java.util.Iterator:next(...)@500 != null
                  * 
                  *  Postconditions:
                  *    return_value != null
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.parser.irc.IRCStringConverter:equalsIgnoreCase(...)@501: {0}, {1}
                  */
   498          final String nick = ClientInfo.parseHost(host);
   499  
   500          for (Query query : queries) {
   501              if (converter.equalsIgnoreCase(ClientInfo.parseHost(query.getHost()), nick)) {
   502                  return query;
   503              }
   504          }
   505  
   506          throw new IllegalArgumentException("No such query: " + host);
   507      }
   508  
   509      /**
   510       * Retrieves a list of queries belonging to this server.
   511       *
   512       * @return list of queries belonging to this server
   513       */
   514      public List<Query> getQueries() {
                 /* 
    P/P           *  Method: List getQueries()
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new ArrayList(getQueries#1)
                  *    new ArrayList(getQueries#1) num objects == 1
                  */
   515          return new ArrayList<Query>(queries);
   516      }
   517  
   518      /**
   519       * Adds a raw window to this server.
   520       */
   521      public void addRaw() {
                 /* 
    P/P           *  Method: void addRaw()
                  * 
                  *  Preconditions:
                  *    init'ed(this.raw)
                  *    (soft) com/dmdirc/Main.controller != null
                  *    (soft) init'ed(this.parser)
                  *    (soft) this.window != null
                  * 
                  *  Postconditions:
                  *    this.raw == One-of{&amp;new Raw(addRaw#1), old this.raw}
                  *    this.raw != null
                  *    new FrameContainer$IconChanger(FrameContainer#2) num objects <= 1
                  *    new ListenerList(FrameContainer#1) num objects <= 1
                  *    new Raw(addRaw#1) num objects <= 1
                  *    new Raw(addRaw#1).changer == &amp;new FrameContainer$IconChanger(FrameContainer#2)
                  *    new Raw(addRaw#1).config != null
                  *    init'ed(new Raw(addRaw#1).icon)
                  *    new Raw(addRaw#1).listeners == &amp;new ListenerList(FrameContainer#1)
                  *    init'ed(new Raw(addRaw#1).notification)
                  *    ...
                  * 
                  *  Test Vectors:
                  *    this.raw: Inverse{null}, Addr_Set{null}
                  *    this.parser: Addr_Set{null}, Inverse{null}
                  */
   522          if (raw == null) {
   523              raw = new Raw(this);
   524  
   525              if (parser != null) {
   526                  raw.registerCallbacks();
   527              }
   528          } else {
   529              raw.activateFrame();
   530          }
   531      }
   532  
   533      /**
   534       * Retrieves the raw window associated with this server.
   535       *
   536       * @return The raw window associated with this server.
   537       */
   538      public Raw getRaw() {
                 /* 
    P/P           *  Method: Raw getRaw()
                  * 
                  *  Preconditions:
                  *    init'ed(this.raw)
                  * 
                  *  Postconditions:
                  *    return_value == this.raw
                  *    init'ed(return_value)
                  */
   539          return raw;
   540      }
   541  
   542      /**
   543       * Removes our reference to the raw object (presumably after it has been
   544       * closed).
   545       */
   546      public void delRaw() {
                 /* 
    P/P           *  Method: void delRaw()
                  * 
                  *  Postconditions:
                  *    this.raw == null
                  */
   547          raw = null; //NOPMD
   548      }
   549  
   550      /**
   551       * Removes a specific channel and window from this server.
   552       *
   553       * @param chan channel to remove
   554       */
   555      public void delChannel(final String chan) {
                 /* 
    P/P           *  Method: void delChannel(String)
                  * 
                  *  Preconditions:
                  *    this.channels != null
                  *    this.converter != null
                  *    this.tabCompleter != null
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.ui.input.TabCompletionType.CHANNEL)
                  */
   556          tabCompleter.removeEntry(TabCompletionType.CHANNEL, chan);
   557          channels.remove(converter.toLowerCase(chan));
   558      }
   559  
   560      /**
   561       * Adds a specific channel and window to this server.
   562       *
   563       * @param chan channel to add
   564       */
   565      public void addChannel(final ChannelInfo chan) {
                 /* 
    P/P           *  Method: void addChannel(ChannelInfo)
                  * 
                  *  Preconditions:
                  *    this.myState != null
                  *    init'ed(this.myState.state)
                  *    (soft) chan != null
                  *    (soft) this.channels != null
                  *    (soft) this.converter != null
                  *    (soft) this.tabCompleter != null
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.ui.input.TabCompletionType.CHANNEL)
                  *    getChannel(...).channelInfo@575 != null
                  *    getChannel(...).config@574 != null
                  *    getChannel(...).config@575 != null
                  *    getChannel(...).eventHandler.owner@574 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    java.util.Map:containsKey(...)@445: {0}, {1}
                  */
   566          synchronized (myState) {
   567              if (myState.getState() == ServerState.CLOSING) {
   568                  // Can't join channels while the server is closing
   569                  return;
   570              }
   571          }
   572  
   573          if (hasChannel(chan.getName())) {
   574              getChannel(chan.getName()).setChannelInfo(chan);
   575              getChannel(chan.getName()).selfJoin();
   576          } else {
   577              final Channel newChan = new Channel(this, chan);
   578  
   579              tabCompleter.addEntry(TabCompletionType.CHANNEL, chan.getName());
   580              channels.put(converter.toLowerCase(chan.getName()), newChan);
   581              newChan.show();
   582          }
   583      }
   584  
   585      /**
   586       * Adds a query to this server.
   587       *
   588       * @param host host of the remote client being queried
   589       */
   590      public void addQuery(final String host) {
                 /* 
    P/P           *  Method: void addQuery(String)
                  * 
                  *  Preconditions:
                  *    this.myState != null
                  *    init'ed(this.myState.state)
                  *    (soft) this.converter != null
                  *    (soft) this.queries != null
                  *    (soft) this.tabCompleter != null
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.ui.input.TabCompletionType.QUERY_NICK)
                  */
   591          synchronized (myState) {
   592              if (myState.getState() == ServerState.CLOSING) {
   593                  // Can't open queries while the server is closing
   594                  return;
   595              }
   596          }
   597  
   598          if (!hasQuery(host)) {
   599              final Query newQuery = new Query(this, host);
   600  
   601              tabCompleter.addEntry(TabCompletionType.QUERY_NICK, ClientInfo.parseHost(host));
   602              queries.add(newQuery);
   603          }
   604      }
   605  
   606      /**
   607       * Deletes a query from this server.
   608       *
   609       * @param query The query that should be removed.
   610       */
   611      public void delQuery(final Query query) {
                 /* 
    P/P           *  Method: void delQuery(Query)
                  * 
                  *  Preconditions:
                  *    query != null
                  *    init'ed(query.host)
                  *    this.queries != null
                  *    this.tabCompleter != null
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.ui.input.TabCompletionType.QUERY_NICK)
                  */
   612          tabCompleter.removeEntry(TabCompletionType.QUERY_NICK, query.getNickname());
   613          queries.remove(query);
   614      }
   615  
   616      /** {@inheritDoc} */
   617      @Override
   618      public boolean ownsFrame(final Window target) {
   619          // Check if it's our server frame
                 /* 
    P/P           *  Method: bool ownsFrame(Window)
                  * 
                  *  Preconditions:
                  *    init'ed(this.window)
                  *    (soft) this.channels != null
                  *    (soft) this.queries != null
                  *    (soft) init'ed(this.raw)
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:next(...)@624 != null
                  *    java.util.Iterator:next(...)@628 != null
                  *    java.util.Map:values(...)@624 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    this.raw: Addr_Set{null}, Inverse{null}
                  *    this.window: Addr_Set{null}, Inverse{null}
                  *    java.lang.Object:equals(...)@215: {0}, {1}
                  *    java.lang.Object:equals(...)@620: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@624: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@628: {0}, {1}
                  */
   620          if (window != null && window.equals(target)) { return true; }
   621          // Check if it's the raw frame
   622          if (raw != null && raw.ownsFrame(target)) { return true; }
   623          // Check if it's a channel frame
   624          for (Channel channel : channels.values()) {
   625              if (channel.ownsFrame(target)) { return true; }
   626          }
   627          // Check if it's a query frame
   628          for (Query query : queries) {
   629              if (query.ownsFrame(target)) { return true; }
   630          }
   631          return false;
   632      }
   633  
   634      /**
   635       * Sets the specified frame as the most-recently activated.
   636       *
   637       * @param source The frame that was activated
   638       */
   639      public void setActiveFrame(final FrameContainer source) {
                 /* 
    P/P           *  Method: void setActiveFrame(FrameContainer)
                  * 
                  *  Postconditions:
                  *    this.activeFrame == source
                  *    init'ed(this.activeFrame)
                  */
   640          activeFrame = source;
   641      }
   642  
   643      /**
   644       * Retrieves a list of all children of this server instance.
   645       *
   646       * @return A list of this server's children
   647       */
   648      public List<WritableFrameContainer> getChildren() {
                 /* 
    P/P           *  Method: List getChildren()
                  * 
                  *  Preconditions:
                  *    this.channels != null
                  *    init'ed(this.raw)
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new ArrayList(getChildren#1)
                  *    new ArrayList(getChildren#1) num objects == 1
                  * 
                  *  Test Vectors:
                  *    this.raw: Addr_Set{null}, Inverse{null}
                  */
   649          final List<WritableFrameContainer> res = new ArrayList<WritableFrameContainer>();
   650  
   651          if (raw != null) {
   652              res.add(raw);
   653          }
   654  
   655          res.addAll(channels.values());
   656          res.addAll(queries);
   657  
   658          return res;
   659      }
   660  
   661      /**
   662       * Closes all open channel windows associated with this server.
   663       */
   664      private void closeChannels() {
                 /* 
    P/P           *  Method: void closeChannels()
                  * 
                  *  Preconditions:
                  *    this.channels != null
                  * 
                  *  Presumptions:
                  *    java.util.ArrayList:iterator(...)@665 != null
                  *    java.util.Iterator:next(...)@665 != null
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@665: {0}, {1}
                  */
   665          for (Channel channel : new ArrayList<Channel>(channels.values())) {
   666              channel.close();
   667          }
   668      }
   669  
   670      /**
   671       * Clears the nicklist of all open channels.
   672       */
   673      private void clearChannels() {
                 /* 
    P/P           *  Method: void clearChannels()
                  * 
                  *  Preconditions:
                  *    this.channels != null
                  * 
                  *  Presumptions:
                  *    channel.config@674 != null
                  *    channel.listeners@674 != null
                  *    channel.window@674 != null
                  *    java.util.Iterator:next(...)@674 != null
                  *    java.util.Map:values(...)@674 != null
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@674: {0}, {1}
                  */
   674          for (Channel channel : channels.values()) {
   675              channel.resetWindow();
   676          }
   677      }
   678  
   679      /**
   680       * Closes all open query windows associated with this server.
   681       */
   682      private void closeQueries() {
                 /* 
    P/P           *  Method: void closeQueries()
                  * 
                  *  Presumptions:
                  *    java.util.ArrayList:iterator(...)@683 != null
                  *    java.util.Iterator:next(...)@683 != null
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@683: {0}, {1}
                  */
   683          for (Query query : new ArrayList<Query>(queries)) {
   684              query.close();
   685          }
   686      }
   687  
   688      // </editor-fold>
   689  
   690      // <editor-fold defaultstate="collapsed" desc="Miscellaneous methods">
   691  
   692      /**
   693       * Construsts a {@link ServerInfo} object for the specified details.
   694       *
   695       * @param server The hostname or IP address of the server
   696       * @param port The port of the server
   697       * @param password The password to use, if any
   698       * @param ssl Whether or not to use SSL
   699       * @return An appropriately configured ServerInfo instance
   700       */
   701      private ServerInfo buildServerInfo(final String server, final int port,
   702              final String password, final boolean ssl) {
                 /* 
    P/P           *  Method: ServerInfo buildServerInfo(String, int, String, bool)
                  * 
                  *  Preconditions:
                  *    this.config != null
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new ServerInfo(buildServerInfo#1)
                  *    new ServerInfo(buildServerInfo#1) num objects == 1
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@706: {0}, {1}
                  */
   703          final ServerInfo myInfo = new ServerInfo(server, port, password);
   704          myInfo.setSSL(ssl);
   705  
   706          if (getConfigManager().hasOptionString(DOMAIN_SERVER, "proxy.address")) {
   707              myInfo.setUseSocks(true);
   708  
   709              myInfo.setProxyHost(getConfigManager()
   710                      .getOption(DOMAIN_SERVER, "proxy.address"));
   711              myInfo.setProxyUser(getConfigManager()
   712                      .getOption(DOMAIN_SERVER, "proxy.user"));
   713              myInfo.setProxyPass(getConfigManager()
   714                      .getOption(DOMAIN_SERVER, "proxy.password"));
   715              myInfo.setProxyPort(getConfigManager()
   716                      .getOptionInt(DOMAIN_SERVER, "proxy.port"));
   717          }
   718  
   719          return myInfo;
   720      }
   721  
   722      /**
   723       * Builds an appropriately configured {@link IRCParser} for this server.
   724       *
   725       * @return A configured IRC parser.
   726       */
   727      private IRCParser buildParser() {
                 /* 
    P/P           *  Method: IRCParser buildParser()
                  * 
                  *  Preconditions:
                  *    this.config != null
                  *    this.parserFactory != null
                  *    this.profile != null
                  *    this.serverInfo != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.ConfigManager:getOptionInt(...)@740/com.dmdirc.parser.irc.IRCParser:getPingTimerLength(...)@740 in range
                  *    com.dmdirc.config.ConfigManager:getOptionInt(...)@740/com.dmdirc.parser.irc.IRCParser:getPingTimerLength(...)@740 in {-231..232-1}
                  *    com.dmdirc.parser.irc.IRCParser:getPingTimerLength(...)@740 != 0
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new IRCParser(getParser#1)
                  *    new IRCParser(getParser#1) num objects == 1
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@743: {0}, {1}
                  */
   728          final CertificateManager certManager = new CertificateManager(serverInfo.getHost(),
   729                  getConfigManager());
   730  
   731          final MyInfo myInfo = buildMyInfo();
   732          final IRCParser myParser = parserFactory.getParser(myInfo, serverInfo);
   733          myParser.setTrustManager(new TrustManager[]{certManager});
   734          myParser.setKeyManagers(certManager.getKeyManager());
   735          myParser.setRemoveAfterCallback(true);
   736          myParser.setCreateFake(true);
   737          myParser.setIgnoreList(ignoreList);
   738          myParser.setPingTimerLength(getConfigManager().getOptionInt(DOMAIN_SERVER,
   739                  "pingtimer"));
   740          myParser.setPingCountDownLength((int) (getConfigManager().getOptionInt(DOMAIN_SERVER,
   741                  "pingfrequency") / myParser.getPingTimerLength()));
   742  
   743          if (getConfigManager().hasOptionString(DOMAIN_GENERAL, "bindip")) {
   744              myParser.setBindIP(getConfigManager().getOption(DOMAIN_GENERAL, "bindip"));
   745          }
   746  
   747          return myParser;
   748      }
   749  
   750      /**
   751       * Retrieves the MyInfo object used for the IRC Parser.
   752       *
   753       * @return The MyInfo object for our profile
   754       */
   755      @Precondition({
   756          "The current profile is not null",
   757          "The current profile specifies at least one nickname"
   758      })
   759      private MyInfo buildMyInfo() {
                 /* 
    P/P           *  Method: MyInfo buildMyInfo()
                  * 
                  *  Preconditions:
                  *    this.profile != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.Identity:getOptionList(...)@761 != null
                  *    com.dmdirc.config.Identity:getOptionList(...)@764 != null
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new MyInfo(buildMyInfo#1)
                  *    new MyInfo(buildMyInfo#1) num objects == 1
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.Identity:hasOptionString(...)@767: {0}, {1}
                  */
   760          Logger.assertTrue(profile != null);
   761          Logger.assertTrue(!profile.getOptionList(DOMAIN_PROFILE, "nicknames").isEmpty());
   762  
   763          final MyInfo myInfo = new MyInfo();
   764          myInfo.setNickname(profile.getOptionList(DOMAIN_PROFILE, "nicknames").get(0));
   765          myInfo.setRealname(profile.getOption(DOMAIN_PROFILE, "realname"));
   766  
   767          if (profile.hasOptionString(DOMAIN_PROFILE, "ident")) {
   768              myInfo.setUsername(profile.getOption(DOMAIN_PROFILE, "ident"));
   769          }
   770  
   771          return myInfo;
   772      }
   773  
   774      /**
   775       * Updates this server's icon.
   776       */
   777      private void updateIcon() {
                 /* 
    P/P           *  Method: void updateIcon()
                  * 
                  *  Preconditions:
                  *    this.config != null
                  *    this.listeners != null
                  *    this.myState != null
                  *    init'ed(this.myState.state)
                  *    (soft) this.serverInfo != null
                  * 
                  *  Postconditions:
                  *    this.icon == One-of{&amp;"secure-server", &amp;"server", &amp;"server-disconnected"}
                  *    this.icon in Addr_Set{&amp;"server-disconnected",&amp;"server",&amp;"secure-server"}
                  */
   778          final String icon = myState.getState() == ServerState.CONNECTED
   779                      ? serverInfo.getSSL() ? "secure-server" : "server"
   780                      : "server-disconnected";
   781          setIcon(icon);
   782      }
   783  
   784      /**
   785       * Registers callbacks.
   786       */
   787      private void doCallbacks() {
                 /* 
    P/P           *  Method: void doCallbacks()
                  * 
                  *  Preconditions:
                  *    this.eventHandler != null
                  *    this.eventHandler.owner != null
                  *    this.queries != null
                  *    init'ed(this.raw)
                  *    (soft) this...parser != null
                  *    (soft) this.eventHandler.owner.server != null
                  *    (soft) this.raw.server != null
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:next(...)@794 != null
                  *    query.server.parser@794 != null
                  *    query.server@794 != null
                  * 
                  *  Test Vectors:
                  *    this.raw: Addr_Set{null}, Inverse{null}
                  *    java.util.Iterator:hasNext(...)@794: {0}, {1}
                  */
   788          if (raw != null) {
   789              raw.registerCallbacks();
   790          }
   791  
   792          eventHandler.registerCallbacks();
   793  
   794          for (Query query : queries) {
   795              query.reregister();
   796          }
   797      }
   798  
   799      /**
   800       * Joins the specified channel, or adds it to the auto-join list if the
   801       * server is not connected.
   802       *
   803       * @param channel The channel to be joined
   804       */
   805      public void join(final String channel) {
                 /* 
    P/P           *  Method: void join(String)
                  * 
                  *  Preconditions:
                  *    this.myState != null
                  *    init'ed(this.myState.state)
                  *    (soft) this.autochannels != null
                  *    (soft) this.channels != null
                  *    (soft) this.converter != null
                  *    (soft) this.invites != null
                  *    (soft) this.listeners != null
                  *    (soft) this.parser != null
                  * 
                  *  Presumptions:
                  *    getChannel(...).channelInfo@811 != null
                  *    getChannel(...).config@811 != null
                  *    getChannel(...).listeners@811 != null
                  *    getChannel(...).server.parser@811 != null
                  *    getChannel(...).server@811 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    java.util.Map:containsKey(...)@445: {0}, {1}
                  */
   806          synchronized (myState) {
   807              if (myState.getState() == ServerState.CONNECTED) {
   808                  removeInvites(channel);
   809  
   810                  if (hasChannel(channel)) {
   811                      getChannel(channel).join();
   812                      getChannel(channel).activateFrame();
   813                  } else {
   814                      parser.joinChannel(channel);
   815                  }
   816              } else {
   817                  autochannels.add(channel);
   818              }
   819          }
   820      }
   821  
   822      /** {@inheritDoc} */
   823      @Override
   824      public void sendLine(final String line) {
                 /* 
    P/P           *  Method: void sendLine(String)
                  * 
                  *  Preconditions:
                  *    init'ed(this.parser)
                  *    (soft) this.myState != null
                  *    (soft) init'ed(this.myState.state)
                  *    (soft) this.window != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.ui.interfaces.ServerWindow:getTranscoder(...)@827 != null
                  */
   825          synchronized (myState) {
   826              if (parser != null && myState.getState() == ServerState.CONNECTED) {
   827                  parser.sendLine(window.getTranscoder().encode(line));
   828              }
   829          }
   830      }
   831  
   832      /** {@inheritDoc} */
   833      @Override
   834      public int getMaxLineLength() {
                 /* 
    P/P           *  Method: int getMaxLineLength()
                  * 
                  *  Postconditions:
                  *    return_value == 510
                  */
   835          return IRCParser.MAX_LINELENGTH;
   836      }
   837  
   838      /**
   839       * Retrieves the parser used for this connection.
   840       *
   841       * @return IRCParser this connection's parser
   842       */
   843      public IRCParser getParser() {
                 /* 
    P/P           *  Method: IRCParser getParser()
                  * 
                  *  Preconditions:
                  *    init'ed(this.parser)
                  * 
                  *  Postconditions:
                  *    return_value == this.parser
                  *    init'ed(return_value)
                  */
   844          return parser;
   845      }
   846  
   847      /**
   848       * Retrieves the profile that's in use for this server.
   849       *
   850       * @return The profile in use by this server
   851       */
   852      public Identity getProfile() {
                 /* 
    P/P           *  Method: Identity getProfile()
                  * 
                  *  Preconditions:
                  *    init'ed(this.profile)
                  * 
                  *  Postconditions:
                  *    return_value == this.profile
                  *    init'ed(return_value)
                  */
   853          return profile;
   854      }
   855  
   856      /**
   857       * Retrieves the name of this server.
   858       *
   859       * @return The name of this server
   860       */
   861      public String getName() {
                 /* 
    P/P           *  Method: String getName()
                  * 
                  *  Preconditions:
                  *    this.serverInfo != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   862          return serverInfo.getHost();
   863      }
   864  
   865      /**
   866       * Retrieves the name of this server's network. The network name is
   867       * determined using the following rules:
   868       *
   869       *  1. If the server includes its network name in the 005 information, we
   870       *     use that
   871       *  2. If the server's name ends in biz, com, info, net or org, we use the
   872       *     second level domain (e.g., foo.com)
   873       *  3. If the server's name contains more than two dots, we drop everything
   874       *     up to and including the first part, and use the remainder
   875       *  4. In all other cases, we use the full server name
   876       *
   877       * @return The name of this server's network
   878       */
   879      public String getNetwork() {
                 /* 
    P/P           *  Method: String getNetwork()
                  * 
                  *  Preconditions:
                  *    this.parser != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.parser.irc.IRCParser:getNetworkName(...)@883 != null
                  *    com.dmdirc.parser.irc.IRCParser:getServerName(...)@884 != null
                  * 
                  *  Postconditions:
                  *    java.lang.StringBuilder:toString(...)._tainted == 0
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    java.lang.String:isEmpty(...)@883: {0}, {1}
                  */
   880          if (parser == null) {
   881              throw new IllegalStateException("getNetwork called when "
   882                      + "parser is null (state: " + getState() + ")");
   883          } else if (parser.getNetworkName().isEmpty()) {
   884              return getNetworkFromServerName(parser.getServerName());
   885          } else {
   886              return parser.getNetworkName();
   887          }
   888      }
   889  
   890      /**
   891       * Determines whether this server is currently connected to the specified
   892       * network.
   893       *
   894       * @param target The network to check for
   895       * @return True if this server is connected to the network, false otherwise
   896       * @since 0.6.3m1rc3
   897       */
   898      public boolean isNetwork(String target) {
                 /* 
    P/P           *  Method: bool isNetwork(String)
                  * 
                  *  Preconditions:
                  *    init'ed(this.parser)
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    this.parser: Inverse{null}, Addr_Set{null}
                  */
   899          synchronized (myState) {
   900              if (parser == null) {
   901                  return false;
   902              } else {
   903                  return getNetwork().equalsIgnoreCase(target);
   904              }
   905          }
   906      }
   907  
   908      /**
   909       * Calculates a network name from the specified server name. This method
   910       * implements parts 2-4 of the procedure documented at getNetwork().
   911       *
   912       * @param serverName The server name to parse
   913       * @return A network name for the specified server
   914       */
   915      protected static String getNetworkFromServerName(final String serverName) {
                 /* 
    P/P           *  Method: String getNetworkFromServerName(String)
                  * 
                  *  Preconditions:
                  *    serverName != null
                  * 
                  *  Postconditions:
                  *    java.lang.StringBuilder:toString(...)._tainted == 0
                  *    return_value == One-of{&amp;java.lang.StringBuilder:toString(...), serverName}
                  *    return_value != null
                  * 
                  *  Test Vectors:
                  *    java.lang.String:endsWith(...)@921: {0}, {1}
                  */
   916          final String[] parts = serverName.split("\\.");
   917          final String[] tlds = {"biz", "com", "info", "net", "org"};
   918          boolean isTLD = false;
   919  
   920          for (String tld : tlds) {
   921              if (serverName.endsWith("." + tld)) {
   922                  isTLD = true;
   923              }
   924          }
   925  
   926          if (isTLD && parts.length > 2) {
   927              return parts[parts.length - 2] + "." + parts[parts.length - 1];
   928          } else if (parts.length > 2) {
   929              final StringBuilder network = new StringBuilder();
   930  
   931              for (int i = 1; i < parts.length; i++) {
   932                  if (network.length() > 0) {
   933                      network.append('.');
   934                  }
   935  
   936                  network.append(parts[i]);
   937              }
   938  
   939              return network.toString();
   940          } else {
   941              return serverName;
   942          }
   943      }
   944  
   945      /**
   946       * Retrieves the name of this server's IRCd.
   947       *
   948       * @return The name of this server's IRCd
   949       */
   950      public String getIrcd() {
                 /* 
    P/P           *  Method: String getIrcd()
                  * 
                  *  Preconditions:
                  *    this.parser != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   951          return parser.getIRCD(true);
   952      }
   953  
   954      /**
   955       * Returns the current away status.
   956       *
   957       * @return True if the client is marked as away, false otherwise
   958       */
   959      public boolean isAway() {
                 /* 
    P/P           *  Method: bool isAway()
                  * 
                  *  Preconditions:
                  *    init'ed(this.awayMessage)
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   960          return awayMessage != null;
   961      }
   962  
   963      /**
   964       * Gets the current away message.
   965       *
   966       * @return Null if the client isn't away, or a textual away message if it is
   967       */
   968      public String getAwayMessage() {
                 /* 
    P/P           *  Method: String getAwayMessage()
                  * 
                  *  Preconditions:
                  *    init'ed(this.awayMessage)
                  * 
                  *  Postconditions:
                  *    return_value == this.awayMessage
                  *    init'ed(return_value)
                  */
   969          return awayMessage;
   970      }
   971  
   972      /**
   973       * Returns the tab completer for this connection.
   974       *
   975       * @return The tab completer for this server
   976       */
   977      public TabCompleter getTabCompleter() {
                 /* 
    P/P           *  Method: TabCompleter getTabCompleter()
                  * 
                  *  Postconditions:
                  *    return_value == this.tabCompleter
                  *    init'ed(return_value)
                  */
   978          return tabCompleter;
   979      }
   980  
   981      /** {@inheritDoc} */
   982      @Override
   983      public InputWindow getFrame() {
                 /* 
    P/P           *  Method: InputWindow getFrame()
                  * 
                  *  Preconditions:
                  *    init'ed(this.window)
                  * 
                  *  Postconditions:
                  *    return_value == this.window
                  *    init'ed(return_value)
                  */
   984          return window;
   985      }
   986  
   987      /**
   988       * Retrieves the current state for this server.
   989       *
   990       * @return This server's state
   991       */
   992      public ServerState getState() {
                 /* 
    P/P           *  Method: ServerState getState()
                  * 
                  *  Preconditions:
                  *    this.myState != null
                  *    init'ed(this.myState.state)
                  * 
                  *  Postconditions:
                  *    return_value == this.myState.state
                  *    init'ed(return_value)
                  */
   993          return myState.getState();
   994      }
   995  
   996      /**
   997       * Retrieves the status object for this server. Effecting state transitions
   998       * on the object returned by this method will almost certainly cause
   999       * problems.
  1000       *
  1001       * @since 0.6.3m1
  1002       * @return This server's status object.
  1003       */
  1004      public ServerStatus getStatus() {
                 /* 
    P/P           *  Method: ServerStatus getStatus()
                  * 
                  *  Postconditions:
                  *    return_value == this.myState
                  *    init'ed(return_value)
                  */
  1005          return myState;
  1006      }
  1007  
  1008      /** {@inheritDoc} */
  1009      @Override
  1010      public void windowClosing() {
                 /* 
    P/P           *  Method: void windowClosing()
                  * 
                  *  Preconditions:
                  *    init'ed(com/dmdirc/ServerManager.me)
                  *    init'ed(this.parser)
                  *    this.window != null
                  *    this.channels != null
                  *    this.eventHandler != null
                  *    this.eventHandler.owner != null
                  *    this.myState != null
                  *    this.myState.history != null
                  *    init'ed(this.raw)
                  *    (soft) init'ed(com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...])
                  *    ...
                  * 
                  *  Presumptions:
                  *    getServerManager(...).servers != null
                  *    java.util.Arrays:asList(...)@89 != null
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == One-of{old com/dmdirc/ServerManager.me, &amp;new ServerManager(getServerManager#1)}
                  *    com/dmdirc/ServerManager.me != null
                  *    this.icon == One-of{old this.icon, &amp;"secure-server", &amp;"server", &amp;"server-disconnected"}
                  *    this.myState.state == &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#13)
                  *    this.parser == null
                  *    this.window == null
                  *    new ArrayList(ServerManager#1) num objects <= 1
                  *    new ServerManager(getServerManager#1) num objects <= 1
                  *    new ServerManager(getServerManager#1).servers == &amp;new ArrayList(ServerManager#1)
                  * 
                  *  Test Vectors:
                  *    this.parser: Addr_Set{null}, Inverse{null}
                  *    this.raw: Addr_Set{null}, Inverse{null}
                  *    com.dmdirc.parser.irc.IRCParser:isReady(...)@1019: {0}, {1}
                  */
  1011          synchronized (myState) {
  1012              // 1: Make the window non-visible
  1013              window.setVisible(false);
  1014  
  1015              // 2: Remove any callbacks or listeners
  1016              eventHandler.unregisterCallbacks();
  1017  
  1018              // 3: Trigger any actions neccessary
  1019              if (parser != null && parser.isReady()) {
  1020                  disconnect();
  1021              }
  1022  
  1023              myState.transition(ServerState.CLOSING);
  1024          }
  1025          
  1026          closeChannels();
  1027          closeQueries();
  1028          removeInvites();
  1029  
  1030          if (raw != null) {
  1031              raw.close();
  1032          }
  1033  
  1034          // 4: Trigger action for the window closing
  1035          // 5: Inform any parents that the window is closing
  1036          ServerManager.getServerManager().unregisterServer(this);
  1037  
  1038          // 6: Remove the window from the window manager
  1039          WindowManager.removeWindow(window);
  1040  
  1041          // 7: Remove any references to the window and parents
  1042          window = null; //NOPMD
  1043          parser = null; //NOPMD
  1044      }
  1045  
  1046      /**
  1047       * Passes the arguments to the most recently activated frame for this
  1048       * server. If the frame isn't know, or isn't visible, use this frame
  1049       * instead.
  1050       *
  1051       * @param messageType The type of message to send
  1052       * @param args The arguments for the message
  1053       */
  1054      public void addLineToActive(final String messageType, final Object... args) {
                 /* 
    P/P           *  Method: void addLineToActive(String, Object[])
                  * 
                  *  Preconditions:
                  *    init'ed(this.activeFrame)
                  * 
                  *  Presumptions:
                  *    getFrame(...)@1055 != null
                  *    getFrame(...)@1059 != null
                  * 
                  *  Postconditions:
                  *    this.activeFrame == One-of{old this.activeFrame, this}
                  *    this.activeFrame != null
                  * 
                  *  Test Vectors:
                  *    this.activeFrame: Addr_Set{null}, Inverse{null}
                  *    com.dmdirc.ui.interfaces.Window:isVisible(...)@1055: {1}, {0}
                  */
  1055          if (activeFrame == null || !activeFrame.getFrame().isVisible()) {
  1056              activeFrame = this;
  1057          }
  1058  
  1059          activeFrame.getFrame().addLine(messageType, args);
  1060      }
  1061  
  1062      /**
  1063       * Passes the arguments to all frames for this server.
  1064       *
  1065       * @param messageType The type of message to send
  1066       * @param args The arguments of the message
  1067       */
  1068      public void addLineToAll(final String messageType, final Object... args) {
                 /* 
    P/P           *  Method: void addLineToAll(String, Object[])
                  * 
                  *  Preconditions:
                  *    this.channels != null
                  *    this.queries != null
                  * 
                  *  Presumptions:
                  *    channel.window@1069 != null
                  *    java.util.Iterator:next(...)@1069 != null
                  *    java.util.Iterator:next(...)@1073 != null
                  *    java.util.Map:values(...)@1069 != null
                  *    query.window@1073 != null
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@1069: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@1073: {0}, {1}
                  */
  1069          for (Channel channel : channels.values()) {
  1070              channel.getFrame().addLine(messageType, args);
  1071          }
  1072  
  1073          for (Query query : queries) {
  1074              query.getFrame().addLine(messageType, args);
  1075          }
  1076  
  1077          addLine(messageType, args);
  1078      }
  1079  
  1080      /**
  1081       * Replies to an incoming CTCP message.
  1082       *
  1083       * @param source The source of the message
  1084       * @param type The CTCP type
  1085       * @param args The CTCP arguments
  1086       */
  1087      public void sendCTCPReply(final String source, final String type, final String args) {
                 /* 
    P/P           *  Method: void sendCTCPReply(String, String, String)
                  * 
                  *  Preconditions:
                  *    type != null
                  *    (soft) this.config != null
                  *    (soft) this.parser != null
                  * 
                  *  Test Vectors:
                  *    java.lang.String:equalsIgnoreCase(...)@1088: {0}, {1}
                  *    java.lang.String:equalsIgnoreCase(...)@1092: {0}, {1}
                  *    java.lang.String:equalsIgnoreCase(...)@1094: {0}, {1}
                  */
  1088          if (type.equalsIgnoreCase("VERSION")) {
  1089              parser.sendCTCPReply(source, "VERSION", "DMDirc " +
  1090                      getConfigManager().getOption("version", "version")
  1091                      + " - http://www.dmdirc.com/");
  1092          } else if (type.equalsIgnoreCase("PING")) {
  1093              parser.sendCTCPReply(source, "PING", args);
  1094          } else if (type.equalsIgnoreCase("CLIENTINFO")) {
  1095              parser.sendCTCPReply(source, "CLIENTINFO", "VERSION PING CLIENTINFO");
  1096          }
  1097      }
  1098  
  1099      /**
  1100       * Determines if the specified channel name is valid. A channel name is
  1101       * valid if we already have an existing Channel with the same name, or
  1102       * we have a valid parser instance and the parser says it's valid.
  1103       *
  1104       * @param channelName The name of the channel to test
  1105       * @return True if the channel name is valid, false otherwise
  1106       */
  1107      public boolean isValidChannelName(final String channelName) {
                 /* 
    P/P           *  Method: bool isValidChannelName(String)
                  * 
                  *  Preconditions:
                  *    this.channels != null
                  *    this.converter != null
                  *    (soft) init'ed(this.parser)
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
  1108          return hasChannel(channelName)
  1109                  || (parser != null && parser.isValidChannelName(channelName));
  1110      }
  1111  
  1112      /**
  1113       * Returns this server's name.
  1114       *
  1115       * @return A string representation of this server (i.e., its name)
  1116       */
  1117      @Override
  1118      public String toString() {
                 /* 
    P/P           *  Method: String toString()
                  * 
                  *  Preconditions:
                  *    this.serverInfo != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
  1119          return getName();
  1120      }
  1121  
  1122      /**
  1123       * Returns the server instance associated with this frame.
  1124       *
  1125       * @return the associated server connection
  1126       */
  1127      @Override
  1128      public Server getServer() {
                 /* 
    P/P           *  Method: Server getServer()
                  * 
                  *  Postconditions:
                  *    return_value == this
                  *    return_value != null
                  */
  1129          return this;
  1130      }
  1131  
  1132      /** {@inheritDoc} */
  1133      @Override
  1134      protected boolean processNotificationArg(final Object arg, final List<Object> args) {
                 /* 
    P/P           *  Method: bool processNotificationArg(Object, List)
                  * 
                  *  Preconditions:
                  *    (soft) args != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
  1135          if (arg instanceof ClientInfo) {
  1136              final ClientInfo clientInfo = (ClientInfo) arg;
  1137              args.add(clientInfo.getNickname());
  1138              args.add(clientInfo.getIdent());
  1139              args.add(clientInfo.getHost());
  1140              return true;
  1141          } else {
  1142              return super.processNotificationArg(arg, args);
  1143          }
  1144      }
  1145  
  1146      // </editor-fold>
  1147  
  1148      // <editor-fold defaultstate="collapsed" desc="Parser callbacks">
  1149  
  1150      /**
  1151       * Called when the server says that the nickname we're trying to use is
  1152       * already in use.
  1153       *
  1154       * @param nickname The nickname that we were trying to use
  1155       */
  1156      public void onNickInUse(final String nickname) {
                 /* 
    P/P           *  Method: void onNickInUse(String)
                  * 
                  *  Preconditions:
                  *    this.converter != null
                  *    this.parser != null
                  *    (soft) this.profile != null
                  * 
                  *  Presumptions:
                  *    (int) (java.lang.Math:random(...)@1164*10) in {-231..232-1}
                  *    com.dmdirc.config.Identity:getOptionList(...)@1166 != null
                  *    java.util.List:get(...)@1177 != null
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.parser.irc.IRCStringConverter:equalsIgnoreCase(...)@1160: {1}, {0}
                  *    java.lang.String:isEmpty(...)@1177: {1}, {0}
                  *    java.util.Iterator:hasNext(...)@1170: {0}, {1}
                  */
  1157          final String lastNick = parser.getMyNickname();
  1158  
  1159          // If our last nick is still valid, ignore the in use message
  1160          if (!converter.equalsIgnoreCase(lastNick, nickname)) {
  1161              return;
  1162          }
  1163  
  1164          String newNick = lastNick + (int) (Math.random() * 10);
  1165  
  1166          final List<String> alts = profile.getOptionList(DOMAIN_PROFILE, "nicknames");
  1167          int offset = 0;
  1168  
  1169          // Loop so we can check case sensitivity
  1170          for (String alt : alts) {
  1171              offset++;
  1172              if (converter.equalsIgnoreCase(alt, lastNick)) {
  1173                  break;
  1174              }
  1175          }
  1176  
  1177          if (offset < alts.size() && !alts.get(offset).isEmpty()) {
  1178              newNick = alts.get(offset);
  1179          }
  1180  
  1181          parser.setNickname(newNick);
  1182      }
  1183  
  1184      /**
  1185       * Called when the server sends a numeric event.
  1186       *
  1187       * @param numeric The numeric code for the event
  1188       * @param tokens The (tokenised) arguments of the event
  1189       */
  1190      public void onNumeric(final int numeric, final String[] tokens) {
                 /* 
    P/P           *  Method: void onNumeric(int, String[])
                  * 
                  *  Preconditions:
                  *    init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    this.config != null
                  *    this.parser != null
                  *    (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#15).type != null
                  *    (soft) com/dmdirc/Main.controller != null
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  *    (soft) tokens != null
                  *    (soft) tokens.length <= 232-1
                  *    (soft) init'ed(tokens[...])
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    new ArrayList(ServerManager#1) num objects == undefined
                  *    new ArrayList(ServerManager#1) num objects == 0, if init'ed
                  *    new ServerManager(getServerManager#1) num objects == new ArrayList(ServerManager#1) num objects
                  *    new ServerManager(getServerManager#1).servers == undefined
                  *    new ServerManager(getServerManager#1).servers == null
                  * 
                  *  Test Vectors:
                  *    numeric: {100..232-1}, {-231..9}, {10..99}
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@1203: {0}, {1}
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@1205: {0}, {1}
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@1207: {0}, {1}
                  */
  1191          String snumeric = String.valueOf(numeric);
  1192  
  1193          if (numeric < 10) {
  1194              snumeric = "00" + snumeric;
  1195          } else if (numeric < 100) {
  1196              snumeric = "0" + snumeric;
  1197          }
  1198  
  1199          final String withIrcd = "numeric_" + parser.getIRCD(true) + "_" + snumeric;
  1200          final String sansIrcd = "numeric_" + snumeric;
  1201          StringBuffer target = null;
  1202  
  1203          if (getConfigManager().hasOptionString("formatter", withIrcd)) {
  1204              target = new StringBuffer(withIrcd);
  1205          } else if (getConfigManager().hasOptionString("formatter", sansIrcd)) {
  1206              target = new StringBuffer(sansIrcd);
  1207          } else if (getConfigManager().hasOptionString("formatter", "numeric_unknown")) {
  1208              target = new StringBuffer("numeric_unknown");
  1209          }
  1210  
  1211          ActionManager.processEvent(CoreActionType.SERVER_NUMERIC, target, this,
  1212                  Integer.valueOf(numeric), tokens);
  1213  
  1214          if (target != null) {
  1215              handleNotification(target.toString(), (Object[]) tokens);
  1216          }
  1217      }
  1218  
  1219      /**
  1220       * Called when the socket has been closed.
  1221       */
  1222      public void onSocketClosed() {
                 /* 
    P/P           *  Method: void onSocketClosed()
                  * 
                  *  Preconditions:
                  *    (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#19).type != null
                  *    (soft) com/dmdirc/Main.controller != null
                  *    (soft) init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  *    (soft) init'ed(this.awayMessage)
                  *    (soft) this.myState.state != null
                  *    (soft) this.channels != null
                  *    (soft) this.config != null
                  *    (soft) this.eventHandler != null
                  *    (soft) this.eventHandler.owner != null
                  *    ...
                  * 
                  *  Presumptions:
                  *    java.util.Arrays:asList(...)@89 init'ed
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    this.awayMessage == One-of{old this.awayMessage, null}
                  *    init'ed(this.awayMessage)
                  *    this.icon == One-of{old this.icon, &amp;"secure-server", &amp;"server", &amp;"server-disconnected"}
                  *    this.myState.state == One-of{old this.myState.state, &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#7), &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#5)}
                  *    this.myState.state != null
                  *    this.parser == One-of{old this.parser, null}
                  *    this.reconnectTimer == old this.reconnectTimer
                  *    new ArrayList(ServerManager#1) num objects == 0, if init'ed
                  *    new ServerManager(getServerManager#1) num objects == 0, if init'ed
                  *    ...
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@1259: {0}, {1}
                  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@1264: {0}, {1}
                  *    java.lang.Thread:holdsLock(...)@1223: {0}, {1}
                  */
  1223          if (Thread.holdsLock(myState)) {
                     /* 
    P/P               *  Method: void com.dmdirc.Server$3(Server)
                      */
  1224              new Thread(new Runnable() {
  1225                  /** {@inheritDoc} */
  1226                  @Override
  1227                  public void run() {
                             /* 
    P/P                       *  Method: void run()
                              */
  1228                      onSocketClosed();
  1229                  }
  1230              }, "Socket closed deferred thread").start();
  1231              return;
  1232          }
  1233          
  1234          handleNotification("socketClosed", getName());
  1235  
  1236          ActionManager.processEvent(CoreActionType.SERVER_DISCONNECTED, null, this);
  1237  
  1238          eventHandler.unregisterCallbacks();
  1239  
  1240          synchronized (myState) {
  1241              if (myState.getState() == ServerState.CLOSING
  1242                      || myState.getState() == ServerState.DISCONNECTED) {
  1243                  // This has been triggered via .disconect()
  1244                  return;
  1245              }
  1246  
  1247              if (myState.getState() == ServerState.DISCONNECTING) {
  1248                  myState.transition(ServerState.DISCONNECTED);
  1249              } else {
  1250                  myState.transition(ServerState.TRANSIENTLY_DISCONNECTED);
  1251              }
  1252  
  1253              clearChannels();
  1254  
  1255              parser = null;
  1256  
  1257              updateIcon();
  1258  
  1259              if (getConfigManager().getOptionBool(DOMAIN_GENERAL,
  1260                      "closechannelsondisconnect")) {
  1261                  closeChannels();
  1262              }
  1263  
  1264              if (getConfigManager().getOptionBool(DOMAIN_GENERAL,
  1265                      "closequeriesondisconnect")) {
  1266                  closeQueries();
  1267              }
  1268  
  1269              removeInvites();
  1270              updateAwayState(null);
  1271  
  1272              if (getConfigManager().getOptionBool(DOMAIN_GENERAL,
  1273                      "reconnectondisconnect")
  1274                      && myState.getState() == ServerState.TRANSIENTLY_DISCONNECTED) {
  1275                  doDelayedReconnect();
  1276              }
  1277          }
  1278      }
  1279  
  1280      /**
  1281       * Called when an error was encountered while connecting.
  1282       *
  1283       * @param errorInfo The parser's error information
  1284       */
  1285      @Precondition("The current server state is CONNECTING")
  1286      public void onConnectError(final ParserError errorInfo) {
                 /* 
    P/P           *  Method: void onConnectError(ParserError)
                  * 
                  *  Preconditions:
                  *    this.myState.state != null
                  *    this.myState != null
                  *    (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#18).type != null
                  *    (soft) com/dmdirc/Main.controller != null
                  *    (soft) init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    (soft) errorInfo != null
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  *    (soft) this.config != null
                  *    (soft) this.listeners != null
                  *    (soft) this.myState.history != null
                  *    ...
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
                  *    com.dmdirc.parser.irc.ParserError:getException(...)@1308 != null
                  *    java.util.Arrays:asList(...)@89 init'ed
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    this.icon == One-of{old this.icon, &amp;"secure-server", &amp;"server", &amp;"server-disconnected"}
                  *    this.myState.state == One-of{old this.myState.state, &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#5)}
                  *    this.myState.state != null
                  *    this.parser == One-of{old this.parser, null}
                  *    this.reconnectTimer == old this.reconnectTimer
                  *    new ArrayList(ServerManager#1) num objects == 0, if init'ed
                  *    new ServerManager(getServerManager#1) num objects == 0, if init'ed
                  *    new ServerManager(getServerManager#1).servers == null
                  *    new Timer(doDelayedReconnect#4) num objects == undefined
                  *    ...
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.parser.irc.ParserError:getException(...)@1305: Inverse{null}, Addr_Set{null}
                  *    java.net.NoRouteToHostException:instanceof(...)@1312: {0}, {1}
                  *    java.net.SocketException:instanceof(...)@1314: {1}, {0}
                  *    java.net.UnknownHostException:instanceof(...)@1310: {0}, {1}
                  *    javax.net.ssl.SSLException:instanceof(...)@1314: {0}, {1}
                  */
  1287          synchronized (myState) {
  1288              if (myState.getState() == ServerState.CLOSING
  1289                      || myState.getState() == ServerState.DISCONNECTING) {
  1290                  // Do nothing
  1291                  return;
  1292              } else if (myState.getState() != ServerState.CONNECTING) {
  1293                  // Shouldn't happen
  1294                  throw new IllegalStateException("Connect error when not "
  1295                          + "connecting\n\n" + getStatus().getTransitionHistory());
  1296              }
  1297  
  1298              myState.transition(ServerState.TRANSIENTLY_DISCONNECTED);
  1299              parser = null;
  1300  
  1301              updateIcon();
  1302  
  1303              String description;
  1304  
  1305              if (errorInfo.getException() == null) {
  1306                  description = errorInfo.getData();
  1307              } else {
  1308                  final Exception exception = errorInfo.getException();
  1309  
  1310                  if (exception instanceof java.net.UnknownHostException) {
  1311                      description = "Unknown host (unable to resolve)";
  1312                  } else if (exception instanceof java.net.NoRouteToHostException) {
  1313                      description = "No route to host";
  1314                  } else if (exception instanceof java.net.SocketException
  1315                          || exception instanceof javax.net.ssl.SSLException) {
  1316                      description = exception.getMessage();
  1317                  } else {
  1318                      Logger.appError(ErrorLevel.LOW, "Unknown socket error", exception);
  1319                      description = "Unknown error: " + exception.getMessage();
  1320                  }
  1321              }
  1322  
  1323              ActionManager.processEvent(CoreActionType.SERVER_CONNECTERROR, null,
  1324                      this, description);
  1325  
  1326              handleNotification("connectError", getName(), description);
  1327  
  1328              if (getConfigManager().getOptionBool(DOMAIN_GENERAL,
  1329                      "reconnectonconnectfailure")) {
  1330                  doDelayedReconnect();
  1331              }
  1332          }
  1333      }
  1334  
  1335      /**
  1336       * Called when we fail to receive a ping reply within a set period of time.
  1337       */
  1338      public void onPingFailed() {
                 /* 
    P/P           *  Method: void onPingFailed()
                  * 
                  *  Preconditions:
                  *    com/dmdirc/Main.controller != null
                  *    init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    this.parser != null
                  *    this.serverInfo != null
                  *    this.config != null
                  *    (soft) init'ed(com.dmdirc.Server$4__static_init.new int[](Server$4__static_init#1)[...])
                  *    (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#33).type != null
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  *    (soft) this.myState.state != null
                  *    (soft) this.parserThread != null
                  *    ...
                  * 
                  *  Presumptions:
                  *    (int) (java.lang.Math:floor(...)@1339) in {-231..232-1}
                  *    com.dmdirc.ui.interfaces.UIController:getStatusBar(...)@1339 != null
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    this.awayMessage == old this.awayMessage
                  *    this.icon == One-of{old this.icon, &amp;"secure-server", &amp;"server", &amp;"server-disconnected"}
                  *    this.myState.state != null
                  *    this.parser == old this.parser
                  *    this.parser != null
                  *    this.parserThread == old this.parserThread
                  *    this.parserThread != null
                  *    this.profile == old this.profile
                  *    init'ed(this.profile)
                  *    ...
                  */
  1339          Main.getUI().getStatusBar().setMessage("No ping reply from "
  1340                  + getName() + " for over "
  1341                  + ((int) (Math.floor(parser.getPingTime(false) / 1000.0)))
  1342                  + " seconds.", null, 10);
  1343  
  1344          ActionManager.processEvent(CoreActionType.SERVER_NOPING, null, this,
  1345                  Long.valueOf(parser.getPingTime(false)));
  1346  
  1347          if (parser.getPingTime(false)
  1348                   >= getConfigManager().getOptionInt(DOMAIN_SERVER, "pingtimeout")) {
  1349              handleNotification("stonedServer", getName());
  1350              reconnect();
  1351          }
  1352      }
  1353  
  1354      /**
  1355       * Called after the parser receives the 005 headers from the server.
  1356       */
  1357      @Precondition("State is CONNECTING")
  1358      public void onPost005() {
                 /* 
    P/P           *  Method: void onPost005()
                  * 
                  *  Preconditions:
                  *    init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    this.myState.state != null
                  *    this.autochannels != null
                  *    this.config != null
                  *    this.ignoreList != null
                  *    this.listeners != null
                  *    this.myState != null
                  *    this.myState.history != null
                  *    this.myState.state.transitions != null
                  *    this.parser != null
                  *    ...
                  * 
                  *  Presumptions:
                  *    chan.channelInfo@1381 != null
                  *    chan.config@1381 != null
                  *    chan.listeners@1381 != null
                  *    chan.server.parser@1381 != null
                  *    chan.server@1381 != null
                  *    ...
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    init'ed(this.converter)
                  *    this.icon == One-of{old this.icon, &amp;"secure-server", &amp;"server", &amp;"server-disconnected", &amp;"channel"}
                  *    this.icon in Addr_Set{&amp;"secure-server",&amp;"server",&amp;"channel"}
                  *    this.myState.state == &amp;com.dmdirc.ServerState__static_init.new ServerState(ServerState__static_init#3)
                  *    new ArrayList(ServerManager#1) num objects == 0, if init'ed
                  *    new ServerManager(getServerManager#1) num objects == 0, if init'ed
                  *    new ServerManager(getServerManager#1).servers == null
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager:getOptionBool(...)@1380: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@1381: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@1386: {0}, {1}
                  */
  1359          synchronized (myState) {
  1360              if (myState.getState() != ServerState.CONNECTING) {
  1361                  // Shouldn't happen
  1362                  throw new IllegalStateException("Received onPost005 while not "
  1363                          + "connecting\n\n" + myState.getTransitionHistory());
  1364              }
  1365          
  1366              if (myState.getState() != ServerState.CONNECTING) {
  1367                  // We've transitioned while waiting for the lock. Just abort.
  1368                  return;
  1369              }
  1370  
  1371              myState.transition(ServerState.CONNECTED);
  1372  
  1373              updateIcon();
  1374  
  1375              getConfigManager().migrate(parser.getIRCD(true), getNetwork(), getName());
  1376              updateIgnoreList();
  1377  
  1378              converter = parser.getIRCStringConverter();
  1379  
  1380              if (getConfigManager().getOptionBool(DOMAIN_GENERAL, "rejoinchannels")) {
  1381                  for (Channel chan : channels.values()) {
  1382                      chan.join();
  1383                  }
  1384              }
  1385  
  1386              for (String channel : autochannels) {
  1387                  parser.joinChannel(channel);
  1388              }
  1389  
  1390              checkModeAliases();
  1391          }
  1392  
  1393          ActionManager.processEvent(CoreActionType.SERVER_CONNECTED, null, this);
  1394      }
  1395  
  1396      /**
  1397       * Checks that we have the neccessary mode aliases for this server.
  1398       */
  1399      private void checkModeAliases() {
  1400          // Check we have mode aliases
                 /* 
    P/P           *  Method: void checkModeAliases()
                  * 
                  *  Preconditions:
                  *    this.parser != null
                  *    (soft) this.config != null
                  * 
                  *  Presumptions:
                  *    arr$.length@1408 <= 232-1
                  *    arr$.length@1414 <= 232-1
                  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
                  *    com.dmdirc.parser.irc.IRCParser:getUserModeString(...)@1403 != null
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@1409: {1}, {0}
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@1415: {1}, {0}
                  *    java.lang.StringBuffer:length(...)@1423: {-231..0}, {1..232-1}
                  *    java.lang.StringBuffer:length(...)@1428: {-231..0}, {1..232-1}
                  *    java.lang.StringBuffer:length(...)@1429: {-231..0}, {1..232-1}
                  */
  1401          final String modes = parser.getBoolChanModes() + parser.getListChanModes()
  1402                  + parser.getSetOnlyChanModes() + parser.getSetUnsetChanModes();
  1403          final String umodes = parser.getUserModeString();
  1404  
  1405          final StringBuffer missingModes = new StringBuffer();
  1406          final StringBuffer missingUmodes = new StringBuffer();
  1407  
  1408          for (char mode : modes.toCharArray()) {
  1409              if (!getConfigManager().hasOptionString(DOMAIN_SERVER, "mode" + mode)) {
  1410                  missingModes.append(mode);
  1411              }
  1412          }
  1413  
  1414          for (char mode : umodes.toCharArray()) {
  1415              if (!getConfigManager().hasOptionString(DOMAIN_SERVER, "umode" + mode)) {
  1416                  missingUmodes.append(mode);
  1417              }
  1418          }
  1419  
  1420          if (missingModes.length() + missingUmodes.length() > 0) {
  1421              final StringBuffer missing = new StringBuffer("Missing mode aliases: ");
  1422  
  1423              if (missingModes.length() > 0) {
  1424                  missing.append("channel: +");
  1425                  missing.append(missingModes);
  1426              }
  1427  
  1428              if (missingUmodes.length() > 0) {
  1429                  if (missingModes.length() > 0) {
  1430                      missing.append(' ');
  1431                  }
  1432  
  1433                  missing.append("user: +");
  1434                  missing.append(missingUmodes);
  1435              }
  1436  
  1437              Logger.appError(ErrorLevel.LOW, missing.toString() + " ["
  1438                      + parser.getIRCD(true) + "]",
  1439                      new Exception(missing.toString() + "\n" // NOPMD
  1440                      + "Network: " + getNetwork() + "\n"
  1441                      + "IRCd: " + parser.getIRCD(false)
  1442                      + " (" + parser.getIRCD(true) + ")\n"
  1443                      + "Mode alias version: "
  1444                      + getConfigManager().getOption("identity", "modealiasversion")
  1445                      + "\n\n"));
  1446          }
  1447      }
  1448  
  1449      // </editor-fold>
  1450  
  1451      // <editor-fold defaultstate="collapsed" desc="Ignore lists">
  1452  
  1453      /**
  1454       * Retrieves this server's ignore list.
  1455       *
  1456       * @return This server's ignore list
  1457       */
  1458      public IgnoreList getIgnoreList() {
                 /* 
    P/P           *  Method: IgnoreList getIgnoreList()
                  * 
                  *  Postconditions:
                  *    return_value == this.ignoreList
                  *    init'ed(return_value)
                  */
  1459          return ignoreList;
  1460      }
  1461  
  1462      /**
  1463       * Updates this server's ignore list to use the entries stored in the
  1464       * config manager.
  1465       */
  1466      public void updateIgnoreList() {
                 /* 
    P/P           *  Method: void updateIgnoreList()
                  * 
                  *  Preconditions:
                  *    this.config != null
                  *    this.ignoreList != null
                  */
  1467          ignoreList.clear();
  1468          ignoreList.addAll(getConfigManager().getOptionList("network", "ignorelist"));
  1469      }
  1470  
  1471      /**
  1472       * Saves the contents of our ignore list to the network identity.
  1473       */
  1474      public void saveIgnoreList() {
                 /* 
    P/P           *  Method: void saveIgnoreList()
                  * 
                  *  Preconditions:
                  *    this.ignoreList != null
                  *    init'ed(this.ignoreList.ignoreInfo)
                  *    this.parser != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.IdentityManager:getNetworkConfig(...)@1497 != null
                  */
  1475          getNetworkIdentity().setOption("network", "ignorelist", ignoreList.getRegexList());
  1476      }
  1477  
  1478      // </editor-fold>
  1479  
  1480      // <editor-fold defaultstate="collapsed" desc="Identity handling">
  1481  
  1482      /**
  1483       * Retrieves the identity for this server.
  1484       *
  1485       * @return This server's identity
  1486       */
  1487      public Identity getServerIdentity() {
                 /* 
    P/P           *  Method: Identity getServerIdentity()
                  * 
                  *  Preconditions:
                  *    this.serverInfo != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
  1488          return IdentityManager.getServerConfig(getName());
  1489      }
  1490  
  1491      /**
  1492       * Retrieves the identity for this server's network.
  1493       *
  1494       * @return This server's network identity
  1495       */
  1496      public Identity getNetworkIdentity() {
                 /* 
    P/P           *  Method: Identity getNetworkIdentity()
                  * 
                  *  Preconditions:
                  *    this.parser != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
  1497          return IdentityManager.getNetworkConfig(getNetwork());
  1498      }
  1499  
  1500      // </editor-fold>
  1501  
  1502      // <editor-fold defaultstate="collapsed" desc="Invite handling">
  1503  
  1504      /**
  1505       * Adds an invite listener to this server.
  1506       *
  1507       * @param listener The listener to be added
  1508       */
  1509      public void addInviteListener(final InviteListener listener) {
                 /* 
    P/P           *  Method: void addInviteListener(InviteListener)
                  * 
                  *  Preconditions:
                  *    this.listeners != null
                  */
  1510          synchronized (listeners) {
  1511              listeners.add(InviteListener.class, listener);
  1512          }
  1513      }
  1514  
  1515      /**
  1516       * Removes an invite listener from this server.
  1517       *
  1518       * @param listener The listener to be removed
  1519       */
  1520      public void removeInviteListener(final InviteListener listener) {
                 /* 
    P/P           *  Method: void removeInviteListener(InviteListener)
                  * 
                  *  Preconditions:
                  *    this.listeners != null
                  */
  1521          synchronized (listeners) {
  1522              listeners.remove(InviteListener.class, listener);
  1523          }
  1524      }
  1525  
  1526      /**
  1527       * Adds an invite to this server, and fires the appropriate listeners.
  1528       *
  1529       * @param invite The invite to be added
  1530       */
  1531      public void addInvite(final Invite invite) {
                 /* 
    P/P           *  Method: void addInvite(Invite)
                  * 
                  *  Preconditions:
                  *    this.invites != null
                  *    this.listeners != null
                  *    (soft) invite != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.util.ListenerList:get(...)@1542 != null
                  *    java.util.ArrayList:iterator(...)@1533 != null
                  *    java.util.Iterator:next(...)@1533 != null
                  *    java.util.Iterator:next(...)@1542 != null
                  *    oldInvite.channel@1533 != null
                  * 
                  *  Test Vectors:
                  *    java.lang.String:equals(...)@1534: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@1533: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@1542: {1}, {0}
                  */
  1532          synchronized (invites) {
  1533              for (Invite oldInvite : new ArrayList<Invite>(invites)) {
  1534                  if (oldInvite.getChannel().equals(invite.getChannel())) {
  1535                      removeInvite(oldInvite);
  1536                  }
  1537              }
  1538  
  1539              invites.add(invite);
  1540  
  1541              synchronized (listeners) {
  1542                  for (InviteListener listener : listeners.get(InviteListener.class)) {
  1543                      listener.inviteReceived(this, invite);
  1544                  }
  1545              }
  1546          }
  1547      }
  1548  
  1549      /**
  1550       * Removes all invites for the specified channel.
  1551       *
  1552       * @param channel The channel to remove invites for
  1553       */
  1554      public void removeInvites(final String channel) {
                 /* 
    P/P           *  Method: void removeInvites(String)
                  * 
                  *  Preconditions:
                  *    (soft) this.invites != null
                  *    (soft) this.listeners != null
                  * 
                  *  Presumptions:
                  *    invite.channel@1555 != null
                  *    java.util.ArrayList:iterator(...)@1555 != null
                  *    java.util.Iterator:next(...)@1555 != null
                  * 
                  *  Test Vectors:
                  *    java.lang.String:equals(...)@1556: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@1555: {0}, {1}
                  */
  1555          for (Invite invite : new ArrayList<Invite>(invites)) {
  1556              if (invite.getChannel().equals(channel)) {
  1557                  removeInvite(invite);
  1558              }
  1559          }
  1560      }
  1561  
  1562      /**
  1563       * Removes all invites for all channels.
  1564       */
  1565      private void removeInvites() {
                 /* 
    P/P           *  Method: void removeInvites()
                  * 
                  *  Preconditions:
                  *    (soft) this.invites != null
                  *    (soft) this.listeners != null
                  * 
                  *  Presumptions:
                  *    java.util.ArrayList:iterator(...)@1566 != null
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@1566: {0}, {1}
                  */
  1566          for (Invite invite : new ArrayList<Invite>(invites)) {
  1567              removeInvite(invite);
  1568          }
  1569      }
  1570  
  1571      /**
  1572       * Removes an invite from this server, and fires the appropriate listeners.
  1573       *
  1574       * @param invite The invite to be removed
  1575       */
  1576      public void removeInvite(final Invite invite) {
                 /* 
    P/P           *  Method: void removeInvite(Invite)
                  * 
                  *  Preconditions:
                  *    this.invites != null
                  *    this.listeners != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.util.ListenerList:get(...)@1581 != null
                  *    java.util.Iterator:next(...)@1581 != null
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@1581: {1}, {0}
                  */
  1577          synchronized (invites) {
  1578              invites.remove(invite);
  1579  
  1580              synchronized (listeners) {
  1581                  for (InviteListener listener : listeners.get(InviteListener.class)) {
  1582                      listener.inviteExpired(this, invite);
  1583                  }
  1584              }
  1585          }
  1586      }
  1587  
  1588      /**
  1589       * Retusnt the list of invites for this server.
  1590       *
  1591       * @return Invite list
  1592       */
  1593      public List<Invite> getInvites() {
                 /* 
    P/P           *  Method: List getInvites()
                  * 
                  *  Postconditions:
                  *    return_value == this.invites
                  *    init'ed(return_value)
                  */
  1594          return invites;
  1595      }
  1596  
  1597      // </editor-fold>
  1598      
  1599      // <editor-fold defaultstate="collapsed" desc="Away state handling">
  1600  
  1601      /**
  1602       * Adds an away state lisener to this server.
  1603       *
  1604       * @param listener The listener to be added
  1605       */
  1606      public void addAwayStateListener(final AwayStateListener listener) {
                 /* 
    P/P           *  Method: void addAwayStateListener(AwayStateListener)
                  * 
                  *  Preconditions:
                  *    this.listeners != null
                  */
  1607          synchronized (listeners) {
  1608              listeners.add(AwayStateListener.class, listener);
  1609          }
  1610      }
  1611  
  1612      /**
  1613       * Removes an away state lisener from this server.
  1614       *
  1615       * @param listener The listener to be removed
  1616       */
  1617      public void removeAwayStateListener(final AwayStateListener listener) {
                 /* 
    P/P           *  Method: void removeAwayStateListener(AwayStateListener)
                  * 
                  *  Preconditions:
                  *    this.listeners != null
                  */
  1618          synchronized (listeners) {
  1619              listeners.remove(AwayStateListener.class, listener);
  1620          }
  1621      }
  1622  
  1623      /**
  1624       * Updates our away state and fires the relevant listeners.
  1625       *
  1626       * @param message The away message to use, or null if we're not away.
  1627       */
  1628      public void updateAwayState(final String message) {
                 /* 
    P/P           *  Method: void updateAwayState(String)
                  * 
                  *  Preconditions:
                  *    init'ed(this.awayMessage)
                  *    (soft) this.listeners != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.util.ListenerList:get(...)@1638 != null
                  *    com.dmdirc.util.ListenerList:get(...)@1642 != null
                  *    java.util.Iterator:next(...)@1638 != null
                  *    java.util.Iterator:next(...)@1642 != null
                  * 
                  *  Postconditions:
                  *    this.awayMessage == One-of{old this.awayMessage, message}
                  *    init'ed(this.awayMessage)
                  * 
                  *  Test Vectors:
                  *    message: Inverse{null}, Addr_Set{null}
                  *    this.awayMessage: Addr_Set{null}, Inverse{null}
                  *    java.lang.String:equals(...)@1629: {1}, {0}
                  */
  1629          if ((awayMessage != null && awayMessage.equals(message))
  1630                  || (awayMessage == null && message == null)) {
  1631              return;
  1632          }
  1633  
  1634          awayMessage = message;
  1635  
  1636          synchronized (listeners) {
  1637              if (message == null) {
  1638                  for (AwayStateListener listener : listeners.get(AwayStateListener.class)) {
  1639                      listener.onBack();
  1640                  }
  1641              } else {
  1642                  for (AwayStateListener listener : listeners.get(AwayStateListener.class)) {
  1643                      listener.onAway(message);
  1644                  }
  1645              }
  1646          }
  1647      }
  1648  
  1649      // </editor-fold>
  1650  
  1651  }








SofCheck Inspector Build Version : 2.17854
Server.java 2009-Jun-25 01:54:24
Server.class 2009-Sep-02 17:04:17
Server$1.class 2009-Sep-02 17:04:17
Server$2.class 2009-Sep-02 17:04:17
Server$3.class 2009-Sep-02 17:04:17
Server$4.class 2009-Sep-02 17:04:17