You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

WritableFrameContainer.java 12KB

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