View Javadoc

1   /*
2    *  Copyright 2004-2006 Stefan Reuter and others
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *
16   */
17  package org.asteriskjava.manager.internal;
18  
19  import org.asteriskjava.AsteriskVersion;
20  import org.asteriskjava.manager.AsteriskMapping;
21  import org.asteriskjava.manager.action.ManagerAction;
22  import org.asteriskjava.manager.action.UserEventAction;
23  import org.asteriskjava.manager.event.UserEvent;
24  import org.asteriskjava.util.Log;
25  import org.asteriskjava.util.LogFactory;
26  import org.asteriskjava.util.ReflectionUtil;
27  
28  import java.lang.reflect.Field;
29  import java.lang.reflect.Method;
30  import java.util.*;
31  
32  /**
33   * Default implementation of the ActionBuilder interface.
34   *
35   * @author srt
36   * @version $Id$
37   */
38  class ActionBuilderImpl implements ActionBuilder
39  {
40      private static final String LINE_SEPARATOR = "\r\n";
41      private static final String ATTRIBUTES_PROPERTY_NAME = "attributes";
42  
43      /**
44       * Instance logger.
45       */
46      private final Log logger = LogFactory.getLog(getClass());
47      private AsteriskVersion targetVersion;
48  
49      /**
50       * Creates a new ActionBuilder for Asterisk 1.0.
51       */
52      ActionBuilderImpl()
53      {
54          this.targetVersion = AsteriskVersion.ASTERISK_1_0;
55      }
56  
57      public void setTargetVersion(AsteriskVersion targetVersion)
58      {
59          this.targetVersion = targetVersion;
60      }
61  
62      public String buildAction(final ManagerAction action)
63      {
64          return buildAction(action, null);
65      }
66  
67      @SuppressWarnings("unchecked")
68      public String buildAction(final ManagerAction action, final String internalActionId)
69      {
70          StringBuffer sb = new StringBuffer();
71  
72          sb.append("action: ");
73          sb.append(action.getAction());
74          sb.append(LINE_SEPARATOR);
75          if (internalActionId != null)
76          {
77              sb.append("actionid: ");
78              sb.append(ManagerUtil.addInternalActionId(action.getActionId(), internalActionId));
79              sb.append(LINE_SEPARATOR);
80          }
81          else if (action.getActionId() != null)
82          {
83              sb.append("actionid: ");
84              sb.append(action.getActionId());
85              sb.append(LINE_SEPARATOR);
86          }
87  
88          /*
89           * When using the Reflection API to get all of the getters for building
90           * actions to send, we ignore some of the getters
91           */
92          Set<String> ignore = new HashSet<String>();
93          ignore.add("class");
94          ignore.add("action");
95          ignore.add("actionid");
96          ignore.add(ATTRIBUTES_PROPERTY_NAME);
97  
98          // if this is a user event action, we need to grab the internal event,
99          // otherwise do below as normal
100         if (action instanceof UserEventAction)
101         {
102             UserEvent userEvent = ((UserEventAction) action).getUserEvent();
103             appendUserEvent(sb, userEvent);
104 
105             // eventually we may want to add more Map keys for events to ignore
106             // when appending
107             appendGetters(sb, userEvent, ignore);
108         }
109         else
110         {
111             appendGetters(sb, action, ignore);
112         }
113 
114         // actions that have the special getAttributes method will
115         // have their Map appended without a singular key or separator
116         Map<String, Method> getters = ReflectionUtil.getGetters(action.getClass());
117         
118         if(getters.containsKey(ATTRIBUTES_PROPERTY_NAME))
119         {
120             Method getter = getters.get(ATTRIBUTES_PROPERTY_NAME);
121             Object value = null;
122             try
123             {
124                 value = getter.invoke(action);
125             }
126             catch (Exception ex)
127             {
128                 logger.error("Unable to retrieve property '" + ATTRIBUTES_PROPERTY_NAME + "' of " + action.getClass(), ex);
129             }
130             
131             if(value instanceof Map)
132             {
133                 Map<Object,Object> attributes = (Map<Object, Object>)value;
134                 for (Map.Entry<Object, Object> entry : attributes.entrySet())
135                 {
136                     appendString(sb, entry.getKey() == null ? "null" : entry.getKey().toString(),
137                             entry.getValue() == null ? "null" : entry.getValue().toString());
138                 }   
139             }
140         }
141         
142         sb.append(LINE_SEPARATOR);
143         return sb.toString();
144     }
145 
146     private void appendMap(StringBuffer sb, String key, Map<String, String> values)
147     {
148         String singularKey;
149 
150         // strip plural s (i.e. use "variable: " instead of "variables: "
151         if (key.endsWith("s"))
152         {
153             singularKey = key.substring(0, key.length() - 1);
154         }
155         else
156         {
157             singularKey = key;
158         }
159 
160         if (targetVersion.isAtLeast(AsteriskVersion.ASTERISK_1_2))
161         {
162             appendMap12(sb, singularKey, values);
163         }
164         else
165         {
166             appendMap10(sb, singularKey, values);
167         }
168     }
169 
170     private void appendMap10(StringBuffer sb, String singularKey, Map<String, String> values)
171     {
172         Iterator<Map.Entry<String, String>> entryIterator;
173 
174         sb.append(singularKey);
175         sb.append(": ");
176         entryIterator = values.entrySet().iterator();
177         while (entryIterator.hasNext())
178         {
179             Map.Entry<String, String> entry;
180 
181             entry = entryIterator.next();
182             sb.append(entry.getKey());
183             sb.append("=");
184             if (entry.getValue() != null)
185             {
186                 sb.append(entry.getValue());
187             }
188 
189             if (entryIterator.hasNext())
190             {
191                 sb.append("|");
192             }
193         }
194         sb.append(LINE_SEPARATOR);
195     }
196 
197     private void appendMap12(StringBuffer sb, String singularKey, Map<String, String> values)
198     {
199         for (Map.Entry<String, String> entry : values.entrySet())
200         {
201             sb.append(singularKey);
202             sb.append(": ");
203             sb.append(entry.getKey());
204             sb.append("=");
205             if (entry.getValue() != null)
206             {
207                 sb.append(entry.getValue());
208             }
209 
210             sb.append(LINE_SEPARATOR);
211         }
212     }
213 
214     private void appendString(StringBuffer sb, String key, String value)
215     {
216         sb.append(key);
217         sb.append(": ");
218         sb.append(value);
219         sb.append(LINE_SEPARATOR);
220     }
221 
222     private void appendUserEvent(StringBuffer sb, UserEvent event)
223     {
224         Class<?> clazz = event.getClass();
225 
226         String className = clazz.getName();
227         String eventType = className.substring(className.lastIndexOf('.') + 1).toLowerCase(Locale.ENGLISH);
228 
229         if (eventType.endsWith("event"))
230         {
231             eventType = eventType.substring(0, eventType.length() - "event".length());
232         }
233 
234         appendString(sb, "UserEvent", eventType);
235     }
236 
237     @SuppressWarnings("unchecked")
238     private void appendGetters(StringBuffer sb, Object action, Set<String> membersToIgnore)
239     {
240         Map<String, Method> getters = ReflectionUtil.getGetters(action.getClass());
241         for (Map.Entry<String, Method> entry : getters.entrySet())
242         {
243             final String name = entry.getKey();
244             final Method getter = entry.getValue();
245             final Object value;
246 
247             if (membersToIgnore.contains(name))
248             {
249                 continue;
250             }
251 
252             try
253             {
254                 value = getter.invoke(action);
255             }
256             catch (Exception ex)
257             {
258                 logger.error("Unable to retrieve property '" + name + "' of " + action.getClass(), ex);
259                 continue;
260             }
261 
262             if (value == null || value instanceof Class)
263             {
264                 continue;
265             }
266 
267             final String mappedName = mapToAsterisk(getter);
268             if (value instanceof Map)
269             {
270                 appendMap(sb, mappedName, (Map<String, String>) value);
271             }
272             else if (value instanceof String)
273             {
274                 appendString(sb, mappedName, (String) value);
275             }
276             else
277             {
278                 appendString(sb, mappedName, value.toString());
279             }
280         }
281     }
282 
283     private String mapToAsterisk(Method getter)
284     {
285         AsteriskMapping annotation;
286 
287         // check annotation of getter method
288         annotation = getter.getAnnotation(AsteriskMapping.class);
289         if (annotation != null)
290         {
291             return annotation.value();
292         }
293 
294         // check annotation of setter method
295         String setterName = determineSetterName(getter.getName());
296         try
297         {
298             Method setter = getter.getDeclaringClass().getDeclaredMethod(setterName, getter.getReturnType());
299             annotation = setter.getAnnotation(AsteriskMapping.class);
300             if (annotation != null)
301             {
302                 return annotation.value();
303             }
304         }
305         catch (NoSuchMethodException e)
306         {
307             // ok, no setter method
308         }
309 
310         // check annotation of field
311         String fieldName = determineFieldName(getter.getName());
312         try
313         {
314             Field field = getter.getDeclaringClass().getDeclaredField(fieldName);
315             annotation = field.getAnnotation(AsteriskMapping.class);
316             if (annotation != null)
317             {
318                 return annotation.value();
319             }
320         }
321         catch (NoSuchFieldException e)
322         {
323             // ok, no field
324         }
325 
326         return fieldName.toLowerCase(Locale.US);
327     }
328 
329     String determineSetterName(String getterName)
330     {
331         if (getterName.startsWith("get"))
332         {
333             return "set" + getterName.substring(3);
334         }
335         else if (getterName.startsWith("is"))
336         {
337             return "set" + getterName.substring(2);
338         }
339         else
340         {
341             throw new IllegalArgumentException("Getter '" + getterName + "' doesn't start with either 'get' or 'is'");
342         }
343     }
344 
345     String determineFieldName(String accessorName)
346     {
347         if (accessorName.startsWith("get"))
348         {
349             return lcFirst(accessorName.substring(3));
350         }
351         else if (accessorName.startsWith("is"))
352         {
353             return lcFirst(accessorName.substring(2));
354         }
355         else if (accessorName.startsWith("set"))
356         {
357             return lcFirst(accessorName.substring(3));
358         }
359         else
360         {
361             throw new IllegalArgumentException("Accessor '" + accessorName + "' doesn't start with either 'get', 'is' or 'set'");
362         }
363     }
364 
365     /**
366      * Converts the first character to lower case.
367      *
368      * @param s the string to convert.
369      * @return the converted string.
370      */
371     String lcFirst(String s)
372     {
373         if (s == null || s.length() < 1)
374         {
375             return s;
376         }
377 
378         char first = s.charAt(0);
379         return Character.toLowerCase(first) + s.substring(1);
380     }
381 }