001: // ============================================================================
002: // $Id: InvokeNoArgMethod.java,v 1.9 2006/04/26 03:40:37 davidahall Exp $
003: // Copyright (c) 2003-2005 David A. Hall
004: // ============================================================================
005: // The contents of this file are subject to the Common Development and
006: // Distribution License (CDDL), Version 1.0 (the License); you may not use this
007: // file except in compliance with the License. You should have received a copy
008: // of the the License along with this file: if not, a copy of the License is
009: // available from Sun Microsystems, Inc.
010: //
011: // http://www.sun.com/cddl/cddl.html
012: //
013: // From time to time, the license steward (initially Sun Microsystems, Inc.) may
014: // publish revised and/or new versions of the License. You may not use,
015: // distribute, or otherwise make this file available under subsequent versions
016: // of the License.
017: //
018: // Alternatively, the contents of this file may be used under the terms of the
019: // GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
020: // case the provisions of the LGPL are applicable instead of those above. If you
021: // wish to allow use of your version of this file only under the terms of the
022: // LGPL, and not to allow others to use your version of this file under the
023: // terms of the CDDL, indicate your decision by deleting the provisions above
024: // and replace them with the notice and other provisions required by the LGPL.
025: // If you do not delete the provisions above, a recipient may use your version
026: // of this file under the terms of either the CDDL or the LGPL.
027: //
028: // This library is distributed in the hope that it will be useful,
029: // but WITHOUT ANY WARRANTY; without even the implied warranty of
030: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
031: // ============================================================================
032:
033: package net.sf.jga.fn.property;
034:
035: import java.lang.reflect.InvocationTargetException;
036: import java.lang.reflect.Method;
037: import java.text.MessageFormat;
038: import net.sf.jga.fn.BinaryFunctor;
039: import net.sf.jga.fn.EvaluationException;
040: import net.sf.jga.fn.UnaryFunctor;
041: import net.sf.jga.fn.adaptor.ChainBinary;
042: import net.sf.jga.fn.adaptor.ChainUnary;
043:
044: /**
045: * Functor that invokes a no-arg method named at construction and returns the
046: * result or null if the result is void.
047: * <p>
048: * Note that declaring the return type incorrectly can result in
049: * ClassCastExceptions being thrown when the functor is invoked: the compiler
050: * cannot check the return type of a reflectively loaded method.
051: * <p>
052: * Copyright © 2003-2005 David A. Hall
053: *
054: * @author <a href="mailto:davidahall@users.sourceforge.net">David A. Hall</a>
055: **/
056:
057: public class InvokeNoArgMethod<T, R> extends UnaryFunctor<T, R> {
058:
059: static final long serialVersionUID = -2651164047444243205L;
060:
061: // The name of the method
062: private String _methName;
063:
064: // The class on which the method is defined
065: private Class<T> _objclass;
066:
067: // The method being called (lazily populated after deserialization)
068: private transient Method _meth;
069:
070: /**
071: * Builds a InvokeMethod for the given method, which takes no arguments.
072: * @throws IllegalArgumentException if the method name is null or empty
073: */
074:
075: public InvokeNoArgMethod(Class<T> objclass, String methName) {
076: if (methName == null || methName.length() == 0) {
077: String msg = "Must supply method name";
078: throw new IllegalArgumentException(msg);
079: }
080:
081: if (objclass == null) {
082: String msg = "Must supply object class";
083: throw new IllegalArgumentException(msg);
084: }
085:
086: _methName = methName;
087: _objclass = objclass;
088:
089: try {
090: _meth = objclass.getMethod(_methName, new Class[0]);
091: } catch (NoSuchMethodException x) {
092: String msg = "No such method: " + x.getMessage();
093: IllegalArgumentException iax = new IllegalArgumentException(
094: msg);
095: iax.initCause(x);
096: throw iax;
097: }
098: }
099:
100: /**
101: * Builds a InvokeMethod for the given method, which takes no arguments.
102: * @throws IllegalArgumentException if the method name is null or empty,
103: * or if it is not defined for the given class, or if it takes arguments
104: */
105:
106: public InvokeNoArgMethod(Class<T> objClass, Method method) {
107: if (method == null) {
108: String msg = "Must supply method";
109: throw new IllegalArgumentException(msg);
110: }
111:
112: if (objClass == null) {
113: String msg = "Must supply object class";
114: throw new IllegalArgumentException(msg);
115: }
116:
117: if (method.getParameterTypes().length != 0) {
118: String msg = "Method {0} takes arguments";
119: Object[] args = { method.getName() };
120: throw new IllegalArgumentException(MessageFormat.format(
121: msg, args));
122: }
123:
124: if (!method.getDeclaringClass().isAssignableFrom(objClass)) {
125: String msg = "Method {0} not defined for class {1}";
126: Object[] args = { method.getName(), objClass.getName() };
127: throw new IllegalArgumentException(MessageFormat.format(
128: msg, args));
129: }
130:
131: _objclass = objClass;
132: _meth = method;
133: _methName = method.getName();
134: }
135:
136: /**
137: * Returns the name of the property that this functor retrieves.
138: */
139: public String getMethodName() {
140: return _methName;
141: }
142:
143: /**
144: * Returns the type of the method
145: */
146: public Class<R> getReturnType() {
147: return (Class<R>) _meth.getReturnType();
148: }
149:
150: /**
151: * Lazy loads the method
152: */
153: public synchronized Method getMethod() throws NoSuchMethodException {
154: if (_meth == null)
155: _meth = _objclass.getMethod(_methName, new Class[0]);
156:
157: return _meth;
158: }
159:
160: // Unary Functor interface
161:
162: /**
163: * Invokes the method on the given object, and returns the result.
164: * @return the result of the designated method of the object
165: * @throws EvaluationException if the argument does not have the designated
166: * public property, or if it is not of the correct type.
167: */
168: public R fn(T obj) {
169: try {
170: // @SuppressWarnings
171: // There's nothing we can do about this other than warn the users
172: // to make sure that they don't use an inappropriate return type
173: return (R) getMethod().invoke(obj, new Object[0]);
174: } catch (NoSuchMethodException x) {
175: String msg = "No method {0} for class {1} that takes no arguments";
176: Object[] args = new Object[] { _methName,
177: _objclass.getName() };
178: throw new EvaluationException(MessageFormat.format(msg,
179: args), x);
180: } catch (ClassCastException x) {
181: String msg = "ClassCastException: " + _objclass + "."
182: + _methName + "()";
183: throw new EvaluationException(msg, x);
184: } catch (IllegalAccessException x) {
185: String msg = _objclass + "." + _methName
186: + " is not accessible";
187: throw new EvaluationException(msg, x);
188: } catch (InvocationTargetException x) {
189: String xmsg = x.getMessage();
190: String msg = "InvocationException: " + _objclass + "."
191: + _methName + "()"
192: + (xmsg != null ? (":" + xmsg) : "");
193: throw new EvaluationException(msg, x);
194: }
195: }
196:
197: /**
198: * Calls the Visitor's <code>visit(InvokeMethod)</code> method, if it
199: * implements the nested Visitor interface.
200: */
201: public void accept(net.sf.jga.fn.Visitor v) {
202: if (v instanceof InvokeNoArgMethod.Visitor)
203: ((InvokeNoArgMethod.Visitor) v).visit(this );
204: else
205: v.visit(this );
206: }
207:
208: // Object overrides
209:
210: public String toString() {
211: return "InvokeNoArgMethod(" + _meth + ")";
212: }
213:
214: // AcyclicVisitor
215:
216: /**
217: * Interface for classes that may interpret a <b>InvokeMethod</b>
218: * function.
219: */
220: public interface Visitor extends net.sf.jga.fn.Visitor {
221: public void visit(InvokeNoArgMethod host);
222: }
223: }
|