001: package org.andromda.translation.ocl.validation;
002:
003: import java.lang.reflect.Method;
004:
005: import org.andromda.core.common.Introspector;
006: import org.andromda.translation.ocl.syntax.OCLPatterns;
007: import org.apache.commons.lang.StringUtils;
008: import org.apache.commons.lang.exception.ExceptionUtils;
009: import org.apache.log4j.Logger;
010:
011: /**
012: * Dynamically invokes operation and property calls on specified <strong>elements</code>.
013: *
014: * @author Wouter Zoons
015: * @author Chad Brandon
016: */
017: public final class OCLIntrospector {
018: private static final Logger logger = Logger
019: .getLogger(OCLIntrospector.class);
020:
021: /**
022: * Invokes the given <code>feature</code> on the <code>element</code>. Its expected that the feature is either an
023: * operation or a property.
024: */
025: public static final Object invoke(final Object element,
026: String feature) {
027: Object result = null;
028: try {
029: feature = StringUtils.trimToEmpty(feature);
030: if (OCLPatterns.isOperation(feature)) {
031: result = invoke(element, feature, null);
032: } else {
033: result = Introspector.instance().getProperty(element,
034: feature);
035: }
036: } catch (final NullPointerException exception) {
037: // ignore (the result will just be null)
038: } catch (final OCLIntrospectorException throwable) {
039: // Dont catch our own exceptions.
040: // Otherwise get Exception/Cause chain which
041: // can hide the original exception.
042: throw throwable;
043: } catch (Throwable throwable) {
044: throwable = getRootCause(throwable);
045:
046: // If cause is an OCLIntrospectorException re-throw
047: // the exception rather than creating a new one.
048: if (throwable instanceof OCLIntrospectorException) {
049: throw (OCLIntrospectorException) throwable;
050: }
051: throw new OCLIntrospectorException(throwable);
052: }
053: return result;
054: }
055:
056: /**
057: * Invokes the given <code>feature</code> on the specified <code>element</code> taking the given
058: * <code>arguments</code>. If <code>arguments</code> is null its expected that the feature is an empty operation.
059: */
060: public static Object invoke(final Object element, String feature,
061: final Object[] arguments) {
062: Object result = null;
063: try {
064: // check for parenthesis
065: int parenIndex = feature.indexOf('(');
066: if (parenIndex != -1) {
067: feature = feature.substring(0, parenIndex).trim();
068: }
069: result = invokeMethod(element, feature, arguments);
070: } catch (final NullPointerException exception) {
071: // ignore (the result will just be null)
072: } catch (Throwable throwable) {
073: final String message = "Error invoking feature '" + feature
074: + "' on element '" + element + "' with arguments '"
075: + StringUtils.join(arguments, ',') + "'";
076: throwable = getRootCause(throwable);
077: logger.error(message);
078: throw new OCLIntrospectorException(throwable);
079: }
080: return result;
081: }
082:
083: private static final Object invokeMethod(final Object element,
084: final String methodName, final Object[] arguments)
085: throws Exception {
086: Object property = null;
087:
088: if (element != null && methodName != null
089: && methodName.length() > 0) {
090: Class[] argumentTypes = getObjectTypes(arguments);
091:
092: final Method method = element.getClass().getMethod(
093: methodName, argumentTypes);
094: property = method.invoke(element, arguments);
095: }
096:
097: return property;
098: }
099:
100: private static final Class[] getObjectTypes(final Object[] objects) {
101: Class[] objectTypes = null;
102: if (objects != null) {
103: objectTypes = new Class[objects.length];
104: for (int ctr = 0; ctr < objects.length; ctr++) {
105: final Object object = objects[ctr];
106: if (object != null) {
107: objectTypes[ctr] = object.getClass();
108: }
109: }
110: }
111: return objectTypes;
112: }
113:
114: /**
115: * Attempts to retrieve the root cause of the exception, if it can not be
116: * found, the <code>throwable</code> itself is returned.
117: *
118: * @param throwable the exception from which to retrieve the root cause.
119: * @return the root cause of the exception
120: */
121: private static final Throwable getRootCause(Throwable throwable) {
122: Throwable root = ExceptionUtils.getRootCause(throwable);
123: if (root != null) {
124: throwable = root;
125: }
126: return throwable;
127: }
128: }
|