View Javadoc
1   package org.andromda.translation.ocl.validation;
2   
3   import java.lang.reflect.Method;
4   
5   import org.andromda.core.common.Introspector;
6   import org.andromda.translation.ocl.syntax.OCLPatterns;
7   import org.apache.commons.lang.StringUtils;
8   import org.apache.commons.lang.exception.ExceptionUtils;
9   import org.apache.log4j.Logger;
10  
11  
12  /**
13   * Dynamically invokes operation and property calls on specified <strong>elements</code>.
14   *
15   * @author Wouter Zoons
16   * @author Chad Brandon
17   */
18  public final class OCLIntrospector
19  {
20      private static final Logger logger = Logger.getLogger(OCLIntrospector.class);
21  
22      /**
23       * Invokes the given <code>feature</code> on the <code>element</code>. Its expected that the feature is either an
24       * operation or a property.
25       *
26       * @param element
27       * @param feature
28       * @return invoke(element,feature,null)
29       */
30      public static final Object invoke(
31              final Object element,
32              String feature)
33      {
34          Object result = null;
35          try
36          {
37              feature = StringUtils.trimToEmpty(feature);
38              if (OCLPatterns.isOperation(feature))
39              {
40                  result = invoke(element, feature, null);
41              } else
42              {
43                  result = Introspector.instance().getProperty(element, feature);
44              }
45          }
46          catch (final NullPointerException ignore)
47          {
48              // ignore (the result will just be null)
49          }
50          catch (final OCLIntrospectorException throwable)
51          {
52              // Don't catch our own exceptions.
53              // Otherwise get Exception/Cause chain which
54              // can hide the original exception.
55              throw throwable;
56          }
57          catch (Throwable throwable)
58          {
59              throwable = getRootCause(throwable);
60  
61              // If cause is an OCLIntrospectorException re-throw
62              // the exception rather than creating a new one.
63              if (throwable instanceof OCLIntrospectorException)
64              {
65                  throw (OCLIntrospectorException) throwable;
66              }
67              throw new OCLIntrospectorException(throwable);
68          }
69          return result;
70      }
71  
72      /**
73       * Invokes the given <code>feature</code> on the specified <code>element</code> taking the given
74       * <code>arguments</code>. If <code>arguments</code> is null its expected that the feature is an empty operation.
75       *
76       * @param element
77       * @param feature
78       * @param arguments
79       * @return invokeMethod(element,feature,arguments)
80       */
81      public static Object invoke(
82              final Object element,
83              String feature,
84              final Object[] arguments)
85      {
86          Object result = null;
87          try
88          {
89              // check for parenthesis
90              int parenIndex = feature.indexOf('(');
91              if (parenIndex != -1)
92              {
93                  feature = feature.substring(0, parenIndex).trim();
94              }
95              result = invokeMethod(element, feature, arguments);
96          }
97          catch (final NullPointerException exception)
98          {
99              // ignore (the result will just be null)
100         }
101         catch (Throwable throwable)
102         {
103             // At least output the location where the error happened, not the entire stack trace.
104             StackTraceElement[] trace = throwable.getStackTrace();
105             String location = " AT " + trace[0].getClassName() + '.' + trace[0].getMethodName() + ':' + trace[0].getLineNumber();
106             if (throwable.getMessage() != null)
107             {
108                 location += ' ' + throwable.getMessage();
109             }
110             /*final String message =
111                 "Error invoking feature '" + feature + "' on element '" + element + "' with arguments '" +
112                 StringUtils.join(arguments, ',') + "'";*/
113             throwable = getRootCause(throwable);
114             logger.error("OCLIntrospector " + throwable + " invoking " + element + " METHOD " + feature + " WITH " + StringUtils.join(arguments, ',') + location);
115             throw new OCLIntrospectorException(throwable);
116         }
117         return result;
118     }
119 
120     private static final Object invokeMethod(
121             final Object element,
122             final String methodName,
123             final Object[] arguments)
124             throws Exception
125     {
126         Object property = null;
127 
128         if (element != null && StringUtils.isNotBlank(methodName))
129         {
130             Class[] argumentTypes = getObjectTypes(arguments);
131 
132             final Method method = element.getClass().getMethod(methodName, argumentTypes);
133             property = method.invoke(element, arguments);
134         }
135 
136         return property;
137     }
138 
139     private static final Class[] getObjectTypes(final Object[] objects)
140     {
141         Class[] objectTypes = null;
142         if (objects != null)
143         {
144             objectTypes = new Class[objects.length];
145             for (int ctr = 0; ctr < objects.length; ctr++)
146             {
147                 final Object object = objects[ctr];
148                 if (object != null)
149                 {
150                     objectTypes[ctr] = object.getClass();
151                 }
152             }
153         }
154         return objectTypes;
155     }
156 
157     /**
158      * Attempts to retrieve the root cause of the exception, if it can not be
159      * found, the <code>throwable</code> itself is returned.
160      *
161      * @param throwable the exception from which to retrieve the root cause.
162      * @return the root cause of the exception
163      */
164     private static final Throwable getRootCause(Throwable throwable)
165     {
166         Throwable root = ExceptionUtils.getRootCause(throwable);
167         if (root != null)
168         {
169             throwable = root;
170         }
171         return throwable;
172     }
173 }