1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.sf.asterisk.fastagi.reply.impl;
18
19 import java.io.*;
20 import java.util.*;
21 import java.util.regex.*;
22
23 import net.sf.asterisk.fastagi.reply.AGIReply;
24
25 /***
26 * Default implementation of the AGIReply interface.
27 *
28 * @author srt
29 * @version $Id: AGIReplyImpl.java,v 1.2 2005/05/03 21:07:52 srt Exp $
30 */
31 public class AGIReplyImpl implements Serializable, AGIReply
32 {
33 private static final Pattern STATUS_PATTERN = Pattern
34 .compile("^(//d{3})[ -]");
35 private static final Pattern RESULT_PATTERN = Pattern
36 .compile("^200 result= *(//S+)");
37 private static final Pattern PARENTHESIS_PATTERN = Pattern
38 .compile("^200 result=//S* +//((.*)//)");
39 private static final Pattern ADDITIONAL_ATTRIBUTES_PATTERN = Pattern
40 .compile("^200 result=//S* +(//(.*//) )?(.+)$");
41 private static final Pattern ADDITIONAL_ATTRIBUTE_PATTERN = Pattern
42 .compile("(//S+)=(//S+)");
43 private static final Pattern SYNOPSIS_PATTERN = Pattern
44 .compile("^//s*Usage://s*(.*)//s*$");
45 private static final String END_OF_PROPER_USAGE = "520 End of proper usage.";
46
47 private Matcher matcher;
48
49 /***
50 * Serial version identifier.
51 */
52 private static final long serialVersionUID = 3256727294671337012L;
53
54 private List lines;
55 private String firstLine;
56
57 /***
58 * The result, that is the part directly following the "result=" string.
59 */
60 private String result;
61
62 /***
63 * The status code.
64 */
65 private int status;
66
67 /***
68 * Additional attributes contained in this reply, for example endpos.
69 */
70 private Map attributes;
71
72 /***
73 * The contents of the parenthesis.
74 */
75 private String extra;
76
77 /***
78 * In case of status == 520 (invalid command syntax) this attribute contains
79 * the synopsis of the command.
80 */
81 private String synopsis;
82
83 /***
84 * In case of status == 520 (invalid command syntax) this attribute contains
85 * the usage of the command.
86 */
87 private String usage;
88
89 public AGIReplyImpl()
90 {
91
92 }
93
94 public AGIReplyImpl(List lines)
95 {
96 this.lines = lines;
97 try
98 {
99 firstLine = (String) lines.get(0);
100 }
101 catch (Exception e)
102 {
103 }
104 }
105
106 public String getFirstLine()
107 {
108 return firstLine;
109 }
110
111 public List getLines()
112 {
113 return lines;
114 }
115
116 /***
117 * Returns the return code (the result as int).
118 *
119 * @return the return code or -1 if the result is not an int.
120 */
121 public int getResultCode()
122 {
123 String result;
124
125 result = getResult();
126 if (result == null)
127 {
128 return -1;
129 }
130
131 try
132 {
133 return Integer.parseInt(result);
134 }
135 catch (NumberFormatException e)
136 {
137 return -1;
138 }
139 }
140
141 /***
142 * Returns the return code as character.
143 *
144 * @return the return code as character.
145 */
146 public char getResultCodeAsChar()
147 {
148 int resultCode;
149
150 resultCode = getResultCode();
151 if (resultCode < 0)
152 {
153 return 0x0;
154 }
155
156 return (char) resultCode;
157 }
158
159 private boolean resultCreated;
160
161 /***
162 * Returns the result, that is the part directly following the "result="
163 * string.
164 *
165 * @return the result.
166 */
167 public String getResult()
168 {
169 if (resultCreated)
170 {
171 return result;
172 }
173
174 matcher = RESULT_PATTERN.matcher(firstLine);
175 if (matcher.find())
176 {
177 result = matcher.group(1);
178 }
179 resultCreated = true;
180 return result;
181 }
182
183 private boolean statusCreated;
184
185 /***
186 * Returns the status code.<br>
187 * Supported status codes are:
188 * <ul>
189 * <li>200 Success
190 * <li>510 Invalid or unknown command
191 * <li>520 Invalid command syntax
192 * </ul>
193 *
194 * @return the status code.
195 */
196 public int getStatus()
197 {
198 if (statusCreated)
199 {
200 return status;
201 }
202
203 matcher = STATUS_PATTERN.matcher(firstLine);
204 if (matcher.find())
205 {
206 status = Integer.parseInt(matcher.group(1));
207 }
208 statusCreated = true;
209 return status;
210 }
211
212 private boolean attributesCreated;
213
214 /***
215 * Returns an additional attribute contained in the reply.<br>
216 * For example the reply to the StreamFileCommand contains an additional
217 * endpos attribute indicating the frame where the playback was stopped.
218 * This can be retrieved by calling getAttribute("endpos") on the
219 * corresponding reply.
220 *
221 * @param name the name of the attribute to retrieve. The name is case
222 * insensitive.
223 * @return the value of the attribute or <code>null</code> if it is not
224 * set.
225 */
226 public String getAttribute(String name)
227 {
228 if (getStatus() != SC_SUCCESS)
229 {
230 return null;
231 }
232
233 if ("result".equalsIgnoreCase(name))
234 {
235 return getResult();
236 }
237
238 if (!attributesCreated)
239 {
240 matcher = ADDITIONAL_ATTRIBUTES_PATTERN.matcher(firstLine);
241 if (matcher.find())
242 {
243 String s;
244 Matcher attributeMatcher;
245
246 attributes = new HashMap();
247 s = matcher.group(2);
248 attributeMatcher = ADDITIONAL_ATTRIBUTE_PATTERN.matcher(s);
249 while (attributeMatcher.find())
250 {
251 String key;
252 String value;
253
254 key = attributeMatcher.group(1);
255 value = attributeMatcher.group(2);
256 attributes.put(key.toLowerCase(), value);
257 }
258 }
259 attributesCreated = true;
260 }
261
262 if (attributes == null || attributes.isEmpty())
263 {
264 return null;
265 }
266
267 return (String) attributes.get(name.toLowerCase());
268 }
269
270 private boolean extraCreated;
271
272 /***
273 * Returns the text in parenthesis contained in this reply.<br>
274 * The meaning of this property depends on the command sent. Sometimes it
275 * contains a flag like "timeout" or "hangup" or - in case of the
276 * GetVariableCommand - the value of the variable.
277 *
278 * @return the text in the parenthesis or <code>null</code> if not set.
279 */
280 public String getExtra()
281 {
282 if (getStatus() != SC_SUCCESS)
283 {
284 return null;
285 }
286
287 if (extraCreated)
288 {
289 return extra;
290 }
291
292 matcher = PARENTHESIS_PATTERN.matcher(firstLine);
293 if (matcher.find())
294 {
295 extra = matcher.group(1);
296 }
297 extraCreated = true;
298 return extra;
299 }
300
301 private boolean synopsisCreated;
302
303 /***
304 * Returns the synopsis of the command sent if Asterisk expected a different
305 * syntax (getStatus() == SC_INVALID_COMMAND_SYNTAX).
306 *
307 * @return the synopsis of the command sent, <code>null</code> if there
308 * were no syntax errors.
309 */
310 public String getSynopsis()
311 {
312 if (getStatus() != SC_INVALID_COMMAND_SYNTAX)
313 {
314 return null;
315 }
316
317 if (!synopsisCreated)
318 {
319 StringBuffer usageSB;
320
321 if (lines.size() > 1)
322 {
323 String secondLine;
324 Matcher synopsisMatcher;
325
326 secondLine = (String) lines.get(1);
327 synopsisMatcher = SYNOPSIS_PATTERN.matcher(secondLine);
328 if (synopsisMatcher.find())
329 {
330 synopsis = synopsisMatcher.group(1);
331 }
332 }
333 synopsisCreated = true;
334
335 usageSB = new StringBuffer();
336 for (int i = 2; i < lines.size(); i++)
337 {
338 String line;
339
340 line = (String) lines.get(i);
341 if (END_OF_PROPER_USAGE.equals(line))
342 {
343 break;
344 }
345
346 usageSB.append(line.trim());
347 usageSB.append(" ");
348 }
349 usage = usageSB.toString().trim();
350 }
351 return synopsis;
352 }
353
354 /***
355 * Returns the usage of the command sent if Asterisk expected a different
356 * syntax (getStatus() == SC_INVALID_COMMAND_SYNTAX).
357 *
358 * @return the usage of the command sent, <code>null</code> if there were
359 * no syntax errors.
360 */
361 public String getUsage()
362 {
363 return usage;
364 }
365
366 public String toString()
367 {
368 StringBuffer sb;
369
370 sb = new StringBuffer(getClass().getName() + ": ");
371 sb.append("status='" + getStatus() + "'; ");
372 if (status == SC_SUCCESS)
373 {
374 sb.append("result='" + getResult() + "'; ");
375 sb.append("extra='" + getExtra() + "'; ");
376 sb.append("attributes=" + attributes + "; ");
377 }
378 if (status == SC_INVALID_COMMAND_SYNTAX)
379 {
380 sb.append("synopsis='" + getSynopsis() + "'; ");
381 }
382 sb.append("systemHashcode=" + System.identityHashCode(this));
383
384 return sb.toString();
385 }
386 }