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 == &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 |