001: // ============================================================================
002: // $Id: SetProperty.java,v 1.14 2006/08/05 21:36:16 davidahall Exp $
003: // Copyright (c) 2002-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:
041: /**
042: * Binary Functor that sets the named property of the first argument to the
043: * value. The property name and type are set at construction. The return
044: * value will be that which the argument's property setter method returns
045: * (generally either null or the old value).
046: * <p>
047: * Note that declaring the return type incorrectly can result in
048: * ClassCastExceptions being thrown when the functor is invoked: the compiler
049: * cannot check the return type of a reflectively loaded method.
050: * <p>
051: * Copyright © 2002-2005 David A. Hall
052: *
053: * @author <a href="mailto:davidahall@users.sourceforge.net">David A. Hall</a>
054: **/
055:
056: // NOTE: compiling this class yields one unchecked cast warning. It is really
057: // up to the user to declare this class properly (the return type must be
058: // correctly specified)
059: public class SetProperty<T, R> extends BinaryFunctor<T, R, R> {
060:
061: static final long serialVersionUID = 5305970242256716550L;
062:
063: // The property class, used to find the correct Method using reflection
064: private Class<R> _propClass;
065:
066: // The name of the property (without the leading 'set').
067: private String _propName;
068:
069: // The name of the setter method (same as _propName, but with 'set' prefix)
070: private String _methName;
071:
072: // The method to invoke
073: private transient Method _meth;
074:
075: /**
076: * Builds a SetProperty that will return the value of the named property
077: * of an instance of type argType. The property will be of type propType.
078: * @throws IllegalArgumentException if either argument is omitted, or if
079: * there is no such setter method in type argType.
080: */
081: public SetProperty(Class<T> argType, String propName,
082: Class<R> propType) {
083: if (propName == null || propName.length() == 0) {
084: throw new IllegalArgumentException(
085: "Must supply property name");
086: }
087: if (propType == null) {
088: throw new IllegalArgumentException(
089: "Must supply property type");
090: }
091:
092: if (propName.startsWith("set")) {
093: _methName = propName;
094: _propName = propName.substring(3);
095: } else {
096: _propName = propName;
097: _methName = "set" + propName;
098: }
099:
100: _propClass = propType;
101:
102: try {
103: Class[] car = new Class[] { propType };
104: _meth = argType.getMethod(_methName, car);
105: } catch (NoSuchMethodException x) {
106: String msg = "class {0} does not have property \"{1}\" of type {2}";
107: Object[] args = new Object[] { argType.getName(), propName,
108: propType.getName() };
109: IllegalArgumentException iax = new IllegalArgumentException(
110: MessageFormat.format(msg, args));
111: iax.initCause(x);
112: throw iax;
113: }
114: }
115:
116: /**
117: * Returns the name of the property that this functor sets.
118: */
119: public String getPropertyName() {
120: return _propName;
121: }
122:
123: // Binary interface
124:
125: /**
126: * Sets the designated property of the argument to the given value. When
127: * the property's setter method returns a value, then this functor will
128: * return it (otherwise it will return null).
129: * <p>
130: * @return the value returned by the designated property's setter method:
131: * generally it is null, but in some cases it might be the old value
132: * @throws EvaluationException if the argument does not have the designated
133: * public property, or if it is accept the given value.
134: */
135: public R fn(T arg, R val) {
136: try {
137: // @SuppressWarnings
138: // There's nothing we can do about this other than warn the users
139: // to make sure that they don't use an inappropriate return type
140: R ret = (R) getMethod(arg)
141: .invoke(arg, new Object[] { val });
142: return ret;
143: } catch (ClassCastException x) {
144: String msg = "{0}.{1} returns type {2}";
145: Method m = getMethod(arg);
146: Object[] args = new Object[] { arg.getClass().getName(),
147: m.getName(), m.getReturnType().getName() };
148: throw new EvaluationException(MessageFormat.format(msg,
149: args), x);
150: } catch (IllegalAccessException x) {
151: String msg = "{0}.{1} is not accessible";
152: Object[] args = new Object[] { arg.getClass().getName(),
153: getMethod(arg).getName() };
154: throw new EvaluationException(MessageFormat.format(msg,
155: args), x);
156: } catch (InvocationTargetException x) {
157: String msg = "{0}.{1}({2}) failed : " + x.getMessage();
158: Object[] args = new Object[] { arg.getClass().getName(),
159: getMethod(arg).getName(), val };
160: throw new EvaluationException(MessageFormat.format(msg,
161: args), x);
162: }
163: }
164:
165: private Method getMethod(T arg) {
166: if (_meth == null) {
167: try {
168: Class[] car = new Class[] { _propClass }; //new Class[]{R}
169: _meth = arg.getClass().getMethod(_methName, car);
170: } catch (NoSuchMethodException x) {
171: throw new EvaluationException(x);
172: }
173: }
174:
175: return _meth;
176: }
177:
178: /**
179: * Calls the Visitor's <code>visit(SetProperty)</code> method, if it
180: * implements the nested Visitor interface.
181: */
182: public void accept(net.sf.jga.fn.Visitor v) {
183: if (v instanceof SetProperty.Visitor)
184: ((SetProperty.Visitor) v).visit(this );
185: else
186: v.visit(this );
187: }
188:
189: // Object overrides
190:
191: public String toString() {
192: return "SetProperty(" + _methName + ")";
193: }
194:
195: // AcyclicVisitor
196:
197: /**
198: * Interface for classes that may interpret a <b>SetProperty</b>
199: * function.
200: */
201: public interface Visitor extends net.sf.jga.fn.Visitor {
202: public void visit(SetProperty host);
203: }
204: }
|