File Source: WritableFrameContainer.java

     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.interfaces.ActionType;
    27  import com.dmdirc.config.ConfigManager;
    28  import com.dmdirc.logger.ErrorLevel;
    29  import com.dmdirc.logger.Logger;
    30  import com.dmdirc.ui.WindowManager;
    31  import com.dmdirc.ui.interfaces.InputWindow;
    32  import com.dmdirc.ui.interfaces.Window;
    33  
    34  import java.util.ArrayList;
    35  import java.util.List;
    36  
    37  /**
    38   * The writable frame container adds additional methods to the frame container
    39   * class that allow the sending of lines back to whatever the container's
    40   * data source is (e.g. an IRC channel or server).
    41   *
    42   * @author chris
    43   */
         /* 
    P/P   *  Method: Window getFrame()
          * 
          *  Postconditions:
          *    init'ed(return_value)
          */
    44  public abstract class WritableFrameContainer extends FrameContainer {
    45      
    46      /** The name of the server notification target. */
             /* 
    P/P       *  Method: com.dmdirc.WritableFrameContainer__static_init
              * 
              *  Postconditions:
              *    NOTIFICATION_CHANNEL != null
              *    NOTIFICATION_SERVER != null
              */
    47      protected static final String NOTIFICATION_SERVER = "server".intern();
    48      
    49      /** The name of the channel notification target. */
    50      protected static final String NOTIFICATION_CHANNEL = "channel".intern();
    51  
    52      /**
    53       * Creates a new WritableFrameContainer.
    54       * 
    55       * @param icon The icon to use for this container
    56       * @param config The config manager for this container
    57       */
    58      public WritableFrameContainer(final String icon, final ConfigManager config) {
                 /* 
    P/P           *  Method: void com.dmdirc.WritableFrameContainer(String, ConfigManager)
                  * 
                  *  Preconditions:
                  *    config != null
                  * 
                  *  Presumptions:
                  *    init'ed(com/dmdirc/FrameContainer.java.awt.Color.BLACK)
                  * 
                  *  Postconditions:
                  *    this.changer == &new FrameContainer$IconChanger(FrameContainer#2)
                  *    this.config == config
                  *    this.config != null
                  *    this.icon == icon
                  *    init'ed(this.icon)
                  *    this.listeners == &new ListenerList(FrameContainer#1)
                  *    this.notification == com/dmdirc/FrameContainer.java.awt.Color.BLACK
                  *    init'ed(this.notification)
                  *    new FrameContainer$IconChanger(FrameContainer#2) num objects == 1
                  *    new ListenerList(FrameContainer#1) num objects == 1
                  */
    59          super(icon, config);
    60      }
    61      
    62      /**
    63       * Sends a line of text to this container's source.
    64       *
    65       * @param line The line to be sent
    66       */
    67      public abstract void sendLine(String line);
    68      
    69      /**
    70       * Returns the internal frame associated with this object.
    71       *
    72       * @return The internal frame associated with this object
    73       */
    74      @Override
    75      public abstract InputWindow getFrame();
    76      
    77      /**
    78       * Returns the maximum length that a line passed to sendLine() should be,
    79       * in order to prevent it being truncated or causing protocol violations.
    80       *
    81       * @return The maximum line length for this container
    82       */
    83      public abstract int getMaxLineLength();
    84  
    85      /**
    86       * Splits the specified line into chunks that contain a number of bytes
    87       * less than or equal to the value returned by {@link #getMaxLineLength()}.
    88       *
    89       * @param line The line to be split
    90       * @return An ordered list of chunks of the desired length
    91       */
    92      protected List<String> splitLine(final String line) {
                 /* 
    P/P           *  Method: List splitLine(String)
                  * 
                  *  Preconditions:
                  *    line != null
                  * 
                  *  Presumptions:
                  *    java.lang.Math:min(...)@103 >= -231+1
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new ArrayList(splitLine#1)
                  *    new ArrayList(splitLine#1) num objects == 1
                  * 
                  *  Test Vectors:
                  *    java.lang.String:indexOf(...)@95: {-231..-1}, {0..232-1}
                  */
    93          final List<String> result = new ArrayList<String>();
    94  
    95          if (line.indexOf('\n') > -1) {
    96              for (String part : line.split("\n")) {
    97                  result.addAll(splitLine(part));
    98              }
    99          } else {
   100              final StringBuilder remaining = new StringBuilder(line);
   101  
   102              while (remaining.toString().getBytes().length > getMaxLineLength()) {
   103                  int number = Math.min(remaining.length(), getMaxLineLength());
   104  
   105                  while (remaining.substring(0, number).getBytes().length > getMaxLineLength()) {
   106                      number--;
   107                  }
   108  
   109                  result.add(remaining.substring(0, number));
   110                  remaining.delete(0, number);
   111              }
   112  
   113              result.add(remaining.toString());
   114          }
   115  
   116          return result;
   117      }
   118      
   119      /**
   120       * Returns the number of lines that the specified string would be sent as.
   121       *
   122       * @param line The string to be split and sent
   123       * @return The number of lines required to send the specified string
   124       */
   125      public final int getNumLines(final String line) {
                 /* 
    P/P           *  Method: int getNumLines(String)
                  * 
                  *  Preconditions:
                  *    line != null
                  * 
                  *  Postconditions:
                  *    return_value == 0
                  */
   126          final String[] splitLines = line.split("(\n|\r\n|\r)", Integer.MAX_VALUE);
   127          int lines = 0;
   128          
   129          for (String splitLine : splitLines) {
   130              if (getMaxLineLength() <= 0) {
   131                  lines++;
   132              } else {
   133                  lines += (int) Math.ceil(splitLine.getBytes().length
   134                          / (double) getMaxLineLength());
   135              }
   136          }
   137          
   138          return lines;
   139      }
   140      
   141      /**
   142       * Processes and displays a notification.
   143       *
   144       * @param messageType The name of the formatter to be used for the message
   145       * @param actionType The action type to be used
   146       * @param args The arguments for the message
   147       */
   148      public void doNotification(final String messageType,
   149              final ActionType actionType, final Object... args) {
                 /* 
    P/P           *  Method: void doNotification(String, ActionType, Object[])
                  * 
                  *  Preconditions:
                  *    actionType != null
                  *    args != null
                  *    args.length <= 232-1
                  *    init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    this.config != null
                  *    (soft) actionType.type != null
                  *    (soft) init'ed(args[...])
                  *    (soft) com/dmdirc/Main.controller != null
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  * 
                  *  Presumptions:
                  *    java.util.List:toArray(...).length@168 <= 232-1
                  *    java.util.List:toArray(...)@166 != null
                  *    java.util.List:toArray(...)@168 != null
                  * 
                  *  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
                  */
   150          final List<Object> messageArgs = new ArrayList<Object>();
   151          final List<Object> actionArgs = new ArrayList<Object>();
   152          final StringBuffer buffer = new StringBuffer(messageType);
   153  
   154          actionArgs.add(this);
   155  
   156          for (Object arg : args) {
   157              actionArgs.add(arg);
   158  
   159              if (!processNotificationArg(arg, messageArgs)) {
   160                  messageArgs.add(arg);
   161              }
   162          }
   163          
   164          modifyNotificationArgs(actionArgs, messageArgs);
   165  
   166          ActionManager.processEvent(actionType, buffer, actionArgs.toArray());
   167  
   168          handleNotification(buffer.toString(), messageArgs.toArray());
   169      } 
   170      
   171      /**
   172       * Allows subclasses to modify the lists of arguments for notifications.
   173       * 
   174       * @param actionArgs The list of arguments to be passed to the actions system
   175       * @param messageArgs The list of arguments to be passed to the formatter
   176       */
   177      protected void modifyNotificationArgs(final List<Object> actionArgs,
   178              final List<Object> messageArgs) {
   179          // Do nothing
             /* 
    P/P       *  Method: void modifyNotificationArgs(List, List)
              */
   180      }
   181      
   182      /**
   183       * Allows subclasses to process specific types of notification arguments.
   184       * 
   185       * @param arg The argument to be processed
   186       * @param args The list of arguments that any data should be appended to
   187       * @return True if the arg has been processed, false otherwise
   188       */
   189      protected boolean processNotificationArg(final Object arg, final List<Object> args) {
                 /* 
    P/P           *  Method: bool processNotificationArg(Object, List)
                  * 
                  *  Postconditions:
                  *    return_value == 0
                  */
   190          return false;
   191      }
   192      
   193      /**
   194       * Handles general server notifications (i.e., ones not tied to a
   195       * specific window). The user can select where the notifications should
   196       * go in their config.
   197       *
   198       * @param messageType The type of message that is being sent
   199       * @param args The arguments for the message
   200       */
   201      public void handleNotification(final String messageType, final Object... args) {        
                 /* 
    P/P           *  Method: void handleNotification(String, Object[])
                  * 
                  *  Preconditions:
                  *    this.config != null
                  *    (soft) args != null
                  *    (soft) args.length <= 232-1
                  *    (soft) init'ed(args[...])
                  *    (soft) com/dmdirc/Main.controller != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.ConfigManager:getOption(...)@202 != null
                  */
   202          despatchNotification(messageType, getConfigManager().hasOptionString("notifications",
   203                  messageType) ? getConfigManager().getOption("notifications", messageType)
   204                  : "self", args);
   205      }
   206      
   207      /**
   208       * Despatches a notification of the specified type to the specified target.
   209       * 
   210       * @param messageType The type of the message that is being sent
   211       * @param messageTarget The target of the message
   212       * @param args The arguments for the message
   213       */
   214      protected void despatchNotification(final String messageType,
   215              final String messageTarget, final Object... args) {
   216          
                 /* 
    P/P           *  Method: void despatchNotification(String, String, Object[])
                  * 
                  *  Preconditions:
                  *    messageTarget != null
                  *    (soft) args != null
                  *    (soft) args.length <= 232-1
                  *    (soft) init'ed(args[...])
                  *    (soft) com/dmdirc/Main.controller != null
                  *    (soft) this.config != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.ConfigManager:getOption(...)@227 != null
                  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
                  *    init'ed(com.dmdirc.logger.ErrorLevel.MEDIUM)
                  *    com.dmdirc.ui.interfaces.InputWindow:getCommandParser(...)@279 != null
                  *    getFrame(...)@279 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.ui.WindowManager:findCustomWindow(...)@251: Inverse{null}, Addr_Set{null}
                  *    java.lang.String:equals(...)@240: {0}, {1}
                  *    java.lang.String:equals(...)@242: {0}, {1}
                  *    java.lang.String:equals(...)@244: {0}, {1}
                  *    java.lang.String:equals(...)@246: {0}, {1}
                  *    java.lang.String:equals(...)@299: {1}, {0}
                  *    java.lang.String:startsWith(...)@220: {0}, {1}
                  *    java.lang.String:startsWith(...)@226: {0}, {1}
                  *    java.lang.String:startsWith(...)@232: {0}, {1}
                  *    java.lang.String:startsWith(...)@248: {0}, {1}
                  *    ...
                  */
   217          String target = messageTarget;
   218          String format = messageType;
   219          
   220          if (target.startsWith("format:")) {
   221              format = target.substring(7);
   222              format = format.substring(0, format.indexOf(':'));
   223              target = target.substring(8 + format.length());
   224          }
   225  
   226          if (target.startsWith("group:")) {
   227              target = getConfigManager().hasOptionString("notifications", target.substring(6))
   228                      ? getConfigManager().getOption("notifications", target.substring(6))
   229                      : "self";
   230          }
   231          
   232          if (target.startsWith("fork:")) {
   233              for (String newtarget : target.substring(5).split("\\|")) {
   234                  despatchNotification(format, newtarget, args);
   235              }
   236              
   237              return;
   238          }
   239  
   240          if ("self".equals(target)) {
   241              addLine(format, args);
   242          }  else if (NOTIFICATION_SERVER.equals(target)) {
   243              getServer().addLine(format, args);
   244          } else if ("all".equals(target)) {
   245              getServer().addLineToAll(format, args);
   246          } else if ("active".equals(target)) {
   247              getServer().addLineToActive(format, args);
   248          } else if (target.startsWith("window:")) {
   249              final String windowName = target.substring(7);
   250  
   251              Window targetWindow = WindowManager.findCustomWindow(getServer().getFrame(),
   252                      windowName);
   253  
   254              if (targetWindow == null) {
   255                  targetWindow = new CustomWindow(windowName, windowName,
   256                          getServer().getFrame()).getFrame();
   257              }
   258  
   259              targetWindow.addLine(format, args);
   260          } else if (target.startsWith("lastcommand:")) {
   261              final Object[] escapedargs = new Object[args.length];
   262  
   263              for (int i = 0; i < args.length; i++) {
   264                  escapedargs[i] = "\\Q" + args[i] + "\\E";
   265              }
   266  
   267              final String command = String.format(target.substring(12), escapedargs);
   268  
   269              WritableFrameContainer best = this;
   270              long besttime = 0;
   271  
   272              final List<WritableFrameContainer> containers
   273                      = new ArrayList<WritableFrameContainer>();
   274              
   275              containers.add(getServer());
   276              containers.addAll(getServer().getChildren());
   277  
   278              for (WritableFrameContainer container : containers) {
   279                  final long time
   280                          = container.getFrame().getCommandParser().getCommandTime(command);
   281                  if (time > besttime) {
   282                      besttime = time;
   283                      best = container;
   284                  }
   285              }
   286  
   287              best.addLine(format, args);
   288          } else if (target.startsWith(NOTIFICATION_CHANNEL + ":")) {
   289             final String channel = String.format(target.substring(8), args);
   290  
   291             if (getServer().hasChannel(channel)) {
   292                 getServer().getChannel(channel).addLine(messageType, args);
   293             } else {
   294                 addLine(format, args);
   295                 Logger.userError(ErrorLevel.LOW,
   296                         "Invalid notification target for type " + messageType
   297                         + ": channel " + channel + " doesn't exist");
   298             }
   299          } else if (!"none".equals(target)) {
   300              addLine(format, args);
   301              Logger.userError(ErrorLevel.MEDIUM,
   302                      "Invalid notification target for type " + messageType + ": " + target);
   303          }
   304      }          
   305      
   306  }








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