File Source: CallbackObject.java
/*
P/P * Method: com.dmdirc.parser.irc.callbacks.CallbackObject__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.parser.irc.callbacks;
24
25 import com.dmdirc.parser.irc.IRCParser;
26 import com.dmdirc.parser.irc.ParserError;
27 import com.dmdirc.parser.irc.callbacks.interfaces.ICallbackInterface;
28
29 import java.lang.annotation.Annotation;
30 import java.lang.reflect.Constructor;
31 import java.lang.reflect.InvocationTargetException;
32 import java.lang.reflect.Method;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37
38 /**
39 * CallbackObject.
40 * Superclass for all callback types.
41 *
42 * @author Shane Mc Cormack
43 */
44 public class CallbackObject {
45
46 /** The type of callback that this object is operating with. */
47 protected final Class<? extends ICallbackInterface> type;
48
49 /** Arraylist for storing callback information related to the callback. */
50 protected final List<ICallbackInterface> callbackInfo = new ArrayList<ICallbackInterface>();
51
52 /** Reference to the IRCParser that owns this callback. */
53 protected IRCParser myParser;
54 /** Reference to the CallbackManager in charge of this callback. */
55 protected CallbackManager myManager;
56
57 /**
58 * Create a new instance of the Callback Object.
59 *
60 * @param parser IRCParser That owns this callback
61 * @param manager CallbackManager that is in charge of this callback
62 * @param type The type of callback to use
63 * @since 0.6.3m1
64 */
65 protected CallbackObject(final IRCParser parser, final CallbackManager manager,
/*
P/P * Method: void com.dmdirc.parser.irc.callbacks.CallbackObject(IRCParser, CallbackManager, Class)
*
* Postconditions:
* this.callbackInfo == &new ArrayList(CallbackObject#1)
* this.myManager == manager
* init'ed(this.myManager)
* this.myParser == parser
* init'ed(this.myParser)
* this.type == type
* init'ed(this.type)
* new ArrayList(CallbackObject#1) num objects == 1
*/
66 final Class<? extends ICallbackInterface> type) {
67 this.myParser = parser;
68 this.myManager = manager;
69 this.type = type;
70 }
71
72 /**
73 * Add a callback pointer to the appropriate ArrayList.
74 *
75 * @param eMethod OBject to callback to.
76 */
77 protected final void addCallback(final ICallbackInterface eMethod) {
/*
P/P * Method: void addCallback(ICallbackInterface)
*
* Preconditions:
* this.callbackInfo != null
*
* Test Vectors:
* java.util.List:contains(...)@78: {1}, {0}
*/
78 if (!callbackInfo.contains(eMethod)) {
79 callbackInfo.add(eMethod);
80 }
81 }
82
83 /**
84 * Delete a callback pointer from the appropriate ArrayList.
85 *
86 * @param eMethod Object that was being called back to.
87 */
88 protected final void delCallback(final ICallbackInterface eMethod) {
/*
P/P * Method: void delCallback(ICallbackInterface)
*
* Preconditions:
* this.callbackInfo != null
*/
89 callbackInfo.remove(eMethod);
90 }
91
92 /**
93 * Call the OnErrorInfo callback.
94 *
95 * @param errorInfo ParserError object to pass as error.
96 * @return true if error call succeeded, false otherwise
97 */
98 protected final boolean callErrorInfo(final ParserError errorInfo) {
/*
P/P * Method: bool callErrorInfo(ParserError)
*
* Preconditions:
* this.myManager != null
* this.myManager.callbackHash != null
*
* Presumptions:
* getCallbackType(...)@99 init'ed
*
* Postconditions:
* init'ed(return_value)
*/
99 return myManager.getCallbackType("OnErrorInfo").call(errorInfo);
100 }
101
102 /**
103 * Add a new callback.
104 *
105 * @param eMethod Object to callback to.
106 */
/*
P/P * Method: void add(ICallbackInterface)
*
* Preconditions:
* this.callbackInfo != null
*/
107 public void add(final ICallbackInterface eMethod) { addCallback(eMethod); }
108
109 /**
110 * Remove a callback.
111 *
112 * @param eMethod Object to remove callback to.
113 */
/*
P/P * Method: void del(ICallbackInterface)
*
* Preconditions:
* this.callbackInfo != null
*/
114 public void del(final ICallbackInterface eMethod) { delCallback(eMethod); }
115
116 /**
117 * Get the name for this callback.
118 *
119 * @return Name of callback
120 */
121 public String getName() {
/*
P/P * Method: String getName()
*
* Preconditions:
* this.type != null
*
* Presumptions:
* java.lang.Class:getSimpleName(...)@122 != null
*
* Postconditions:
* java.lang.StringBuilder:toString(...)._tainted == 0
* return_value == &java.lang.StringBuilder:toString(...)
*/
122 return "On" + type.getSimpleName().substring(1); // Trim the 'I'
123 }
124
125 /**
126 * Get the name for this callback in lowercase.
127 *
128 * @return Name of callback, in lowercase
129 */
/*
P/P * Method: String getLowerName()
*
* Preconditions:
* this.type != null
*
* Postconditions:
* return_value != null
*/
130 public final String getLowerName() { return this.getName().toLowerCase(); }
131
132 /**
133 * Actually calls this callback. The specified arguments must match those
134 * specified in the callback's interface, or an error will be raised.
135 *
136 * @param args The arguments to pass to the callback implementation
137 * @return True if a method was called, false otherwise
138 */
139 public boolean call(final Object ... args) {
/*
P/P * Method: bool call(Object[])
*
* Preconditions:
* args != null
* this.myParser != null
* init'ed(this.myParser.createFake)
* (soft) init'ed(args[...])
* (soft) this.myManager != null
* (soft) this.myManager.callbackHash != null
* (soft) init'ed(this.myParser.lastLine)
* (soft) this.type != null
*
* Presumptions:
* java.lang.Class:getMethods(...).length@152 >= 1
* java.lang.Class:getMethods(...)@152 != null
* java.lang.Class:getMethods(...)[0]@152 != null
* java.util.ArrayList:iterator(...)@150 != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* this.myParser.createFake: {0}, {1}
* java.util.Iterator:hasNext(...)@150: {0}, {1}
*/
140 boolean bResult = false;
141
142 final Object[] newArgs = new Object[args.length + 1];
143 System.arraycopy(args, 0, newArgs, 1, args.length);
144 newArgs[0] = myParser;
145
146 if (myParser.getCreateFake()) {
147 createFakeArgs(newArgs);
148 }
149
150 for (ICallbackInterface iface : new ArrayList<ICallbackInterface>(callbackInfo)) {
151 try {
152 type.getMethods()[0].invoke(iface, newArgs);
153 } catch (Exception e) {
154 final ParserError ei = new ParserError(ParserError.ERROR_ERROR,
155 "Exception in callback ("+e.getMessage()+")", myParser.getLastLine());
156 ei.setException(e);
157 callErrorInfo(ei);
158 }
159 bResult = true;
160 }
161 return bResult;
162 }
163
164 /**
165 * Replaces all null entries in the specified array with fake values,
166 * if the corresponding parameter of this callback's type is marked with
167 * the {@link FakableArgument} annotation. The fake classes are constructed
168 * by using parameters designated {@link FakableSource}.
169 *
170 * @param args The arguments to be faked
171 */
172 protected void createFakeArgs(final Object[] args) {
/*
P/P * Method: void createFakeArgs(Object[])
*
* Preconditions:
* this.type != null
* (soft) args != null
* (soft) args.length >= 1
* (soft) init'ed(args[...])
*
* Presumptions:
* arr$.length@175 <= args.length
* arr$.length@175 <= 232-1
* arr$.length@175 in {1..232-1}
* arr$.length@175 >= 1
* arr$[i$]@175 != null
* ...
*
* Postconditions:
* init'ed(args[...])
*
* Test Vectors:
* java.lang.Object:equals(...)@177: {0}, {1}
*/
173 int i = 0;
174
175 for (Annotation[] anns : type.getMethods()[0].getParameterAnnotations()) {
176 for (Annotation ann : anns) {
177 if (ann.annotationType().equals(FakableArgument.class)
178 && args[i] == null) {
179 args[i] = getFakeArg(args, type.getMethods()[0].getParameterTypes()[i]);
180 }
181 }
182
183 i++;
184 }
185 }
186
187 /**
188 * Tries to create fake argument of the specified target class, by using
189 * {@link FakableSource} denoted parameters from the specified arg array.
190 *
191 * If an argument is missing, the method attempts to create a fake instance
192 * by recursing into this method again. Note that this could cause an
193 * infinite recursion in cases of cyclic dependencies. If recursion fails,
194 * the constructor is skipped.
195 *
196 * If the created object has a <code>setFake(boolean)</code> method, it
197 * is automatically invoked with an argument of <code>true</code>.
198 *
199 * @param args The arguments array to use for sources
200 * @param target The class that should be constructed
201 * @return An instance of the target class, or null on failure
202 */
203 protected Object getFakeArg(final Object[] args, final Class<?> target) {
/*
P/P * Method: Object getFakeArg(Object[], Class)
*
* Preconditions:
* target != null
* this.type != null
* (soft) args != null
* (soft) args.length >= 1
* (soft) init'ed(args[...])
*
* Presumptions:
* arr$.length@207 in {1..232-1}
* arr$.length@207 <= 232-1
* arr$.length@207 <= args.length
* arr$.length@207 - arr$.length@207 in {0..232-1}
* arr$.length@217 <= 232-1
* ...
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* java.lang.Object:equals(...)@209: {0}, {1}
* java.lang.Object:equals(...)@241: {0}, {1}
* java.lang.String:equals(...)@241: {0}, {1}
* java.lang.reflect.Method:getParameterTypes(...).length@241: {0, 2..+Inf}, {1}
* java.util.Map:containsKey(...)@225: {0}, {1}
*/
204 final Map<Class, Object> sources = new HashMap<Class, Object>();
205 int i = 0;
206
207 for (Annotation[] anns : type.getMethods()[0].getParameterAnnotations()) {
208 for (Annotation ann : anns) {
209 if (ann.annotationType().equals(FakableSource.class)) {
210 sources.put(type.getMethods()[0].getParameterTypes()[i], args[i]);
211 }
212 }
213
214 i++;
215 }
216
217 for (Constructor<?> ctor : target.getConstructors()) {
218 Object[] params = new Object[ctor.getParameterTypes().length];
219
220 i = 0;
221 Object param;
222 boolean failed = false;
223
224 for (Class<?> needed : ctor.getParameterTypes()) {
225 if (sources.containsKey(needed)) {
226 params[i] = sources.get(needed);
227 } else if ((param = getFakeArg(args, needed)) != null) {
228 params[i] = param;
229 } else {
230 failed = true;
231 }
232
233 i++;
234 }
235
236 if (!failed) {
237 try {
238 final Object instance = ctor.newInstance(params);
239
240 for (Method method : target.getMethods()) {
241 if (method.getName().equals("setFake")
242 && method.getParameterTypes().length == 1
243 && method.getParameterTypes()[0].equals(Boolean.TYPE)) {
244
245 method.invoke(instance, true);
246 }
247 }
248
249 return instance;
250 } catch (InstantiationException ex) {
251 // Do nothing
252 } catch (IllegalAccessException ex) {
253 // Do nothing
254 } catch (IllegalArgumentException ex) {
255 // Do nothing
256 } catch (InvocationTargetException ex) {
257 // Do nothing
258 }
259 }
260 }
261
262 return null;
263 }
264
265 }
SofCheck Inspector Build Version : 2.17854
| CallbackObject.java |
2009-Jun-25 01:54:24 |
| CallbackObject.class |
2009-Sep-02 17:04:12 |