File Source: Query.java
/*
P/P * Method: com.dmdirc.Query__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.commandparser.CommandManager;
28 import com.dmdirc.commandparser.CommandType;
29 import com.dmdirc.logger.ErrorLevel;
30 import com.dmdirc.logger.Logger;
31 import com.dmdirc.parser.irc.ClientInfo;
32 import com.dmdirc.parser.irc.IRCParser;
33 import com.dmdirc.parser.irc.callbacks.CallbackManager;
34 import com.dmdirc.parser.irc.callbacks.CallbackNotFoundException;
35 import com.dmdirc.parser.irc.callbacks.interfaces.INickChanged;
36 import com.dmdirc.parser.irc.callbacks.interfaces.IPrivateAction;
37 import com.dmdirc.parser.irc.callbacks.interfaces.IPrivateMessage;
38 import com.dmdirc.parser.irc.callbacks.interfaces.IQuit;
39 import com.dmdirc.ui.WindowManager;
40 import com.dmdirc.ui.input.TabCompleter;
41 import com.dmdirc.ui.input.TabCompletionType;
42 import com.dmdirc.ui.interfaces.InputWindow;
43 import com.dmdirc.ui.interfaces.QueryWindow;
44
45 import java.awt.Toolkit;
46 import java.io.Serializable;
47
48 /**
49 * The Query class represents the client's view of a query with another user.
50 * It handles callbacks for query events from the parser, maintains the
51 * corresponding QueryWindow, and handles user input for the query.
52 * @author chris
53 */
/*
P/P * Method: Window getFrame()
*
* Preconditions:
* init'ed(this.window)
*
* Postconditions:
* return_value == this.window
* init'ed(return_value)
*/
54 public final class Query extends MessageTarget implements
55 IPrivateAction, IPrivateMessage, INickChanged, IQuit, Serializable {
56
57 /**
58 * A version number for this class. It should be changed whenever the class
59 * structure is changed (or anything else that would prevent serialized
60 * objects being unserialized with the new class).
61 */
62 private static final long serialVersionUID = 1;
63
64 /** The Server this Query is on. */
65 private Server server;
66
67 /** The QueryWindow used for this Query. */
68 private QueryWindow window;
69
70 /** The full host of the client associated with this Query. */
71 private String host;
72
73 /** The tab completer for the query window. */
74 private final TabCompleter tabCompleter;
75
76 /**
77 * Creates a new instance of Query.
78 *
79 * @param newHost host of the remove client
80 * @param newServer The server object that this Query belongs to
81 */
82 public Query(final Server newServer, final String newHost) {
/*
P/P * Method: void com.dmdirc.Query(Server, String)
*
* Preconditions:
* com/dmdirc/Main.controller != null
* init'ed(com/dmdirc/actions/ActionManager.killSwitch)
* newServer != null
* newServer.parser != null
* init'ed(newServer.window)
* (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#61).type != null
* (soft) init'ed(com/dmdirc/ServerManager.me)
*
* Presumptions:
* com.dmdirc.Server:getConfigManager(...)@83 != null
* com.dmdirc.Server:getConfigManager(...)@93 != null
* init'ed(com.dmdirc.commandparser.CommandType.TYPE_CHAT)
* init'ed(com.dmdirc.commandparser.CommandType.TYPE_QUERY)
* init'ed(com.dmdirc.ui.input.TabCompletionType.COMMAND)
* ...
*
* Postconditions:
* com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
* this.changer == &new FrameContainer$IconChanger(FrameContainer#2)
* this.config != null
* this.host == newHost
* init'ed(this.host)
* this.icon == &"query"
* this.listeners == &new ListenerList(FrameContainer#1)
* this.notification == com/dmdirc/FrameContainer.java.awt.Color.BLACK
* init'ed(this.notification)
* this.server == newServer
* ...
*
* Test Vectors:
* com.dmdirc.config.ConfigManager:getOptionBool(...)@93: {1}, {0}
*/
83 super("query", newServer.getConfigManager());
84
85 this.server = newServer;
86 this.host = newHost;
87
88 window = Main.getUI().getQuery(this);
89 WindowManager.addWindow(server.getFrame(), window);
90
91 ActionManager.processEvent(CoreActionType.QUERY_OPENED, null, this);
92
93 if (!server.getConfigManager().getOptionBool("general", "hidequeries")) {
94 window.open();
95 }
96
97 tabCompleter = new TabCompleter(server.getTabCompleter());
98 tabCompleter.addEntries(TabCompletionType.COMMAND,
99 CommandManager.getCommandNames(CommandType.TYPE_QUERY));
100 tabCompleter.addEntries(TabCompletionType.COMMAND,
101 CommandManager.getCommandNames(CommandType.TYPE_CHAT));
102 window.getInputHandler().setTabCompleter(tabCompleter);
103
104 reregister();
105
106 updateTitle();
107 }
108
109 /**
110 * Shows this query's window.
111 */
112 public void show() {
/*
P/P * Method: void show()
*
* Preconditions:
* this.window != null
*/
113 window.open();
114 }
115
116 /** {@inheritDoc} */
117 @Override
118 public InputWindow getFrame() {
/*
P/P * Method: InputWindow getFrame()
*
* Preconditions:
* init'ed(this.window)
*
* Postconditions:
* return_value == this.window
* init'ed(return_value)
*/
119 return window;
120 }
121
122 /**
123 * Returns the tab completer for this query.
124 *
125 * @return This query's tab completer
126 */
127 public TabCompleter getTabCompleter() {
/*
P/P * Method: TabCompleter getTabCompleter()
*
* Postconditions:
* return_value == this.tabCompleter
* init'ed(return_value)
*/
128 return tabCompleter;
129 }
130
131 /** {@inheritDoc} */
132 @Override
133 public void sendLine(final String line) {
/*
P/P * Method: void sendLine(String)
*
* Preconditions:
* this.server != null
* this.server.myState != null
* init'ed(this.server.myState.state)
* (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#65).type != null
* (soft) init'ed(com/dmdirc/actions/ActionManager.killSwitch)
* (soft) init'ed(this.host)
* (soft) this.server.parser != null
* (soft) this.window != null
*
* Presumptions:
* com.dmdirc.parser.irc.IRCParser:getMyself(...)@139 != null
* com.dmdirc.ui.interfaces.QueryWindow:getTranscoder(...)@141 != null
* com.dmdirc.util.StringTranscoder:encode(...)@141 != null
* java.awt.Toolkit:getDefaultToolkit(...)@135 != null
*
* Postconditions:
* com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
* init'ed(new ArrayList(ServerManager#1) num objects)
* init'ed(new ServerManager(getServerManager#1) num objects)
* new ServerManager(getServerManager#1).servers == null
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@141: {0}, {1}
*/
134 if (server.getState() != ServerState.CONNECTED) {
135 Toolkit.getDefaultToolkit().beep();
136 return;
137 }
138
139 final ClientInfo client = server.getParser().getMyself();
140
141 for (String part : splitLine(window.getTranscoder().encode(line))) {
142 server.getParser().sendMessage(ClientInfo.parseHost(host),
143 part);
144
145 final StringBuffer buff = new StringBuffer("querySelfMessage");
146
147 ActionManager.processEvent(CoreActionType.QUERY_SELF_MESSAGE, buff, this, part);
148
149 addLine(buff, client.getNickname(), client.getIdent(),
150 client.getHost(), part);
151 }
152 }
153
154 /** {@inheritDoc} */
155 @Override
156 public int getMaxLineLength() {
/*
P/P * Method: int getMaxLineLength()
*
* Preconditions:
* this.server != null
* this.server.myState != null
* init'ed(this.server.myState.state)
* (soft) init'ed(this.host)
* (soft) this.server.parser != null
*
* Postconditions:
* init'ed(return_value)
*/
157 return server.getState() == ServerState.CONNECTED ? server.getParser()
158 .getMaxLength("PRIVMSG", host) : -1;
159 }
160
161 /**
162 * Sends a private action to the remote user.
163 *
164 * @param action action text to send
165 */
166 @Override
167 public void sendAction(final String action) {
/*
P/P * Method: void sendAction(String)
*
* Preconditions:
* this.server != null
* this.server.myState != null
* init'ed(this.server.myState.state)
* (soft) action != null
* (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#66).type != null
* (soft) init'ed(com/dmdirc/actions/ActionManager.killSwitch)
* (soft) init'ed(com/dmdirc/ServerManager.me)
* (soft) init'ed(this.host)
* (soft) this.server.parser != null
* (soft) this.window != null
*
* Presumptions:
* com.dmdirc.parser.irc.IRCParser:getMyself(...)@173 != null
* com.dmdirc.ui.interfaces.QueryWindow:getTranscoder(...)@177 != null
* com.dmdirc.ui.interfaces.QueryWindow:getTranscoder(...)@183 != null
* java.awt.Toolkit:getDefaultToolkit(...)@169 != null
*
* Postconditions:
* com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
* 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
*/
168 if (server.getState() != ServerState.CONNECTED) {
169 Toolkit.getDefaultToolkit().beep();
170 return;
171 }
172
173 final ClientInfo client = server.getParser().getMyself();
174 final int maxLineLength = server.getParser().getMaxLength("PRIVMSG", host);
175
176 if (maxLineLength >= action.length() + 2) {
177 server.getParser().sendAction(ClientInfo.parseHost(host), window.getTranscoder().encode(action));
178
179 final StringBuffer buff = new StringBuffer("querySelfAction");
180
181 ActionManager.processEvent(CoreActionType.QUERY_SELF_ACTION, buff, this, action);
182
183 addLine(buff, client.getNickname(), client.getIdent(),
184 client.getHost(), window.getTranscoder().encode(action));
185 } else {
186 addLine("actionTooLong", action.length());
187 }
188 }
189
190 /**
191 * Handles a private message event from the parser.
192 *
193 * @param parser Parser receiving the event
194 * @param message message received
195 * @param remoteHost remote user host
196 */
197 @Override
198 public void onPrivateMessage(final IRCParser parser, final String message,
199 final String remoteHost) {
/*
P/P * Method: void onPrivateMessage(IRCParser, String, String)
*
* Preconditions:
* init'ed(com/dmdirc/actions/ActionManager.killSwitch)
* (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#63).type != null
* (soft) init'ed(com/dmdirc/ServerManager.me)
*
* Presumptions:
* com.dmdirc.parser.irc.ClientInfo:parseHostFull(...)@200 != null
* parts.length@200 >= 3
*
* 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
*/
200 final String[] parts = ClientInfo.parseHostFull(remoteHost);
201
202 final StringBuffer buff = new StringBuffer("queryMessage");
203
204 ActionManager.processEvent(CoreActionType.QUERY_MESSAGE, buff, this, message);
205
206 addLine(buff, parts[0], parts[1], parts[2], message);
207 }
208
209 /**
210 * Handles a private action event from the parser.
211 *
212 * @param parser Parser receiving the event
213 * @param message message received
214 * @param remoteHost remote host
215 */
216 @Override
217 public void onPrivateAction(final IRCParser parser, final String message,
218 final String remoteHost) {
/*
P/P * Method: void onPrivateAction(IRCParser, String, String)
*
* Preconditions:
* init'ed(com/dmdirc/actions/ActionManager.killSwitch)
* init'ed(this.host)
* (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#64).type != null
* (soft) init'ed(com/dmdirc/ServerManager.me)
*
* Presumptions:
* com.dmdirc.parser.irc.ClientInfo:parseHostFull(...)@219 != null
* parts.length@219 >= 3
*
* 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
*/
219 final String[] parts = ClientInfo.parseHostFull(host);
220
221 final StringBuffer buff = new StringBuffer("queryAction");
222
223 ActionManager.processEvent(CoreActionType.QUERY_ACTION, buff, this, message);
224
225 addLine(buff, parts[0], parts[1], parts[2], message);
226 }
227
228 /**
229 * Updates the QueryWindow's title.
230 */
231 private void updateTitle() {
/*
P/P * Method: void updateTitle()
*
* Preconditions:
* init'ed(this.host)
* this.window != null
*/
232 final String title = ClientInfo.parseHost(host);
233
234 window.setTitle(title);
235 }
236
237 /**
238 * Reregisters query callbacks. Called when reconnecting to the server.
239 */
240 public void reregister() {
/*
P/P * Method: void reregister()
*
* Preconditions:
* this.server != null
* this.server.parser != null
* (soft) init'ed(this.host)
*
* Presumptions:
* init'ed(com.dmdirc.logger.ErrorLevel.HIGH)
* com.dmdirc.parser.irc.IRCParser:getCallbackManager(...)@241 != null
*/
241 final CallbackManager callbackManager = server.getParser().getCallbackManager();
242
243 try {
244 callbackManager.addCallback("onPrivateAction", this, ClientInfo.parseHost(host));
245 callbackManager.addCallback("onPrivateMessage", this, ClientInfo.parseHost(host));
246 callbackManager.addCallback("onQuit", this);
247 callbackManager.addCallback("onNickChanged", this);
248 } catch (CallbackNotFoundException ex) {
249 Logger.appError(ErrorLevel.HIGH, "Unable to get query events", ex);
250 }
251 }
252
253 /** {@inheritDoc} */
254 @Override
255 public void onNickChanged(final IRCParser tParser, final ClientInfo cClient,
256 final String sOldNick) {
/*
P/P * Method: void onNickChanged(IRCParser, ClientInfo, String)
*
* Preconditions:
* init'ed(this.host)
* sOldNick != null
* (soft) cClient != null
* (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#68).type != null
* (soft) init'ed(com/dmdirc/actions/ActionManager.killSwitch)
* (soft) init'ed(com/dmdirc/ServerManager.me)
* (soft) this.server != null
* (soft) this.server.parser != null
* (soft) this.server.tabCompleter != null
* (soft) this.window != null
*
* Presumptions:
* init'ed(com.dmdirc.logger.ErrorLevel.HIGH)
* com.dmdirc.parser.irc.IRCParser:getCallbackManager(...)@258 != null
* init'ed(com.dmdirc.ui.input.TabCompletionType.QUERY_NICK)
*
* Postconditions:
* com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
* java.lang.StringBuilder:toString(...)._tainted == 0
* this.host == One-of{old this.host, &java.lang.StringBuilder:toString(...)}
* init'ed(this.host)
* 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:
* java.lang.String:equals(...)@257: {0}, {1}
*/
257 if (sOldNick.equals(ClientInfo.parseHost(host))) {
258 final CallbackManager callbackManager = server.getParser().getCallbackManager();
259
260 callbackManager.delCallback("onPrivateAction", this);
261 callbackManager.delCallback("onPrivateMessage", this);
262
263 try {
264 callbackManager.addCallback("onPrivateAction", this, cClient.getNickname());
265 callbackManager.addCallback("onPrivateMessage", this, cClient.getNickname());
266 } catch (CallbackNotFoundException ex) {
267 Logger.appError(ErrorLevel.HIGH, "Unable to get query events", ex);
268 }
269
270 final StringBuffer format = new StringBuffer("queryNickChanged");
271
272 ActionManager.processEvent(CoreActionType.QUERY_NICKCHANGE, format, this, sOldNick);
273
274 server.getTabCompleter().removeEntry(TabCompletionType.QUERY_NICK, sOldNick);
275 server.getTabCompleter().addEntry(TabCompletionType.QUERY_NICK, cClient.getNickname());
276
277 addLine(format, sOldNick, cClient.getIdent(),
278 cClient.getHost(), cClient.getNickname());
279 host = cClient.getNickname() + "!" + cClient.getIdent() + "@" + cClient.getHost();
280 updateTitle();
281 }
282 }
283
284 /** {@inheritDoc} */
285 @Override
286 public void onQuit(final IRCParser tParser, final ClientInfo cClient,
287 final String sReason) {
/*
P/P * Method: void onQuit(IRCParser, ClientInfo, String)
*
* Preconditions:
* cClient != null
* init'ed(this.host)
* (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#67).type != null
* (soft) init'ed(com/dmdirc/actions/ActionManager.killSwitch)
* (soft) init'ed(com/dmdirc/ServerManager.me)
* (soft) sReason != null
*
* Presumptions:
* com.dmdirc.parser.irc.ClientInfo:getNickname(...)@288 != null
*
* Postconditions:
* com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
* 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:
* java.lang.String:equals(...)@288: {0}, {1}
*/
288 if (cClient.getNickname().equals(ClientInfo.parseHost(host))) {
289 final StringBuffer format = new StringBuffer(sReason.isEmpty()
290 ? "queryQuit" : "queryQuitReason");
291
292 ActionManager.processEvent(CoreActionType.QUERY_QUIT, format, this, sReason);
293
294 addLine(format, cClient.getNickname(),
295 cClient.getIdent(), cClient.getHost(), sReason);
296 }
297 }
298
299 /**
300 * Returns the Server associated with this query.
301 *
302 * @return associated Server
303 */
304 @Override
305 public Server getServer() {
/*
P/P * Method: Server getServer()
*
* Preconditions:
* init'ed(this.server)
*
* Postconditions:
* return_value == this.server
* init'ed(return_value)
*/
306 return server;
307 }
308
309 /** {@inheritDoc} */
310 @Override
311 public void windowClosing() {
312 // 1: Make the window non-visible
/*
P/P * Method: void windowClosing()
*
* Preconditions:
* init'ed(com/dmdirc/actions/ActionManager.killSwitch)
* init'ed(this.server)
* this.window != null
* (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#62).type != null
* (soft) init'ed(com/dmdirc/ServerManager.me)
* (soft) init'ed(this.host)
* (soft) init'ed(this.server.parser)
* (soft) this.server.queries != null
* (soft) this.server.tabCompleter != null
*
* Presumptions:
* com.dmdirc.parser.irc.IRCParser:getCallbackManager(...)@317 != null
*
* Postconditions:
* com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
* this.server == null
* this.window == null
* 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:
* this.server: Addr_Set{null}, Inverse{null}
* this.server.parser: Addr_Set{null}, Inverse{null}
*/
313 window.setVisible(false);
314
315 // 2: Remove any callbacks or listeners
316 if (server != null && server.getParser() != null) {
317 server.getParser().getCallbackManager().delAllCallback(this);
318 }
319
320 // 3: Trigger any actions neccessary
321
322 // 4: Trigger action for the window closing
323 ActionManager.processEvent(CoreActionType.QUERY_CLOSED, null, this);
324
325 // 5: Inform any parents that the window is closing
326 if (server != null) {
327 server.delQuery(this);
328 }
329
330 // 6: Remove the window from the window manager
331 WindowManager.removeWindow(window);
332
333 // 7: Remove any references to the window and parents
334 window = null;
335 server = null;
336 }
337
338 /**
339 * Returns this query's name.
340 *
341 * @return A string representation of this query (i.e., the user's name)
342 */
343 @Override
344 public String toString() {
/*
P/P * Method: String toString()
*
* Preconditions:
* init'ed(this.host)
*
* Postconditions:
* init'ed(return_value)
*/
345 return ClientInfo.parseHost(host);
346 }
347
348 /**
349 * Returns the host that this query is with.
350 *
351 * @return The full host that this query is with
352 */
353 public String getHost() {
/*
P/P * Method: String getHost()
*
* Preconditions:
* init'ed(this.host)
*
* Postconditions:
* return_value == this.host
* init'ed(return_value)
*/
354 return host;
355 }
356
357 /**
358 * Returns the current nickname of the user that this query is with.
359 *
360 * @return The nickname of this query's user
361 */
362 public String getNickname() {
/*
P/P * Method: String getNickname()
*
* Preconditions:
* init'ed(this.host)
*
* Postconditions:
* init'ed(return_value)
*/
363 return ClientInfo.parseHost(host);
364 }
365
366 /** {@inheritDoc} */
367 @Override
368 public void activateFrame() {
/*
P/P * Method: void activateFrame()
*
* Preconditions:
* init'ed(this.window)
*
* Test Vectors:
* this.window: Inverse{null}, Addr_Set{null}
* com.dmdirc.ui.interfaces.QueryWindow:isVisible(...)@373: {1}, {0}
*/
369 if (window == null) {
370 return;
371 }
372
373 if (!window.isVisible()) {
374 show();
375 }
376
377 super.activateFrame();
378 }
379
380 }
SofCheck Inspector Build Version : 2.17854
| Query.java |
2009-Jun-25 01:54:24 |
| Query.class |
2009-Sep-02 17:04:17 |