001: // ============================================================================
002: // $Id: SetField.java,v 1.9 2006/08/05 21:44:48 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.Field;
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 field of the first argument to the
043: * value. The field name and type are set at construction. The return
044: * value will be null.
045: * <p>
046: * Note that declaring the return type incorrectly can result in
047: * ClassCastExceptions being thrown when the functor is invoked: the compiler
048: * cannot check the return type of a reflectively loaded field.
049: * <p>
050: * Copyright © 2002-2005 David A. Hall
051: *
052: * @author <a href="mailto:davidahall@users.sourceforge.net">David A. Hall</a>
053: **/
054:
055: // NOTE: compiling this class yields one unchecked cast warning. It is really
056: // up to the user to declare this class properly (the return type must be
057: // correctly specified)
058: public class SetField<T, R> extends BinaryFunctor<T, R, R> {
059:
060: static final long serialVersionUID = -5051261348109487737L;
061:
062: // The field class, used to find the correct Field using reflection
063: private Class<R> _fieldType;
064:
065: // The name of the field
066: private String _fieldName;
067:
068: // The field to set
069: private transient Field _field;
070:
071: /**
072: * Builds a SetField that will update the value of the given field
073: * @throws IllegalArgumentException if either argument is omitted, or if
074: * there is no such setter field in type argType.
075: */
076: public SetField(Field field) {
077: this ((Class<T>) field.getDeclaringClass(), field,
078: (Class<R>) field.getType());
079: }
080:
081: /**
082: * Builds a SetField that will update the value of the given field
083: * @throws IllegalArgumentException if either argument is omitted, or if
084: * there is no such setter field in type argType.
085: */
086: public SetField(Class<T> argType, Field field) {
087: this (argType, field, (Class<R>) field.getType());
088: }
089:
090: /**
091: * Builds a SetField that will update the value of the given field
092: * @throws IllegalArgumentException if either argument is omitted, or if
093: * there is no such setter field in type argType.
094: */
095: public SetField(Class<T> argType, Field field, Class<R> fieldType) {
096: if (field == null) {
097: throw new IllegalArgumentException("Must supply field");
098: }
099:
100: if (!field.getDeclaringClass().isAssignableFrom(argType)) {
101: throw new IllegalArgumentException(buildNoSuchFieldMessage(
102: field.getName(), argType));
103: }
104:
105: _fieldName = field.getName();
106: _fieldType = fieldType;
107: _field = checkFieldType(field, fieldType);
108: }
109:
110: /**
111: * Builds a SetField that will update the value of the named field
112: * of an instance of type argType. The field will be of type propType.
113: * @throws IllegalArgumentException if either argument is omitted, or if
114: * there is no such setter field in type argType.
115: */
116: public SetField(Class<T> argType, String fieldName,
117: Class<R> fieldType) {
118: if (fieldName == null || fieldName.length() == 0) {
119: throw new IllegalArgumentException("Must supply field name");
120: }
121: if (fieldType == null) {
122: throw new IllegalArgumentException("Must supply field type");
123: }
124:
125: _fieldName = fieldName;
126: _fieldType = fieldType;
127:
128: try {
129: _field = checkFieldType(argType.getField(_fieldName),
130: fieldType);
131: } catch (NoSuchFieldException x) {
132: IllegalArgumentException iax = new IllegalArgumentException(
133: buildNoSuchFieldMessage(fieldName, argType));
134: iax.initCause(x);
135: throw iax;
136: }
137: }
138:
139: private Field checkFieldType(Field field, Class<R> fieldType)
140: throws IllegalArgumentException {
141: if (!field.getType().isAssignableFrom(fieldType)) {
142: String msg = "Field {0} is of the wrong type: is {1}, expected {2}";
143: Object[] args = new Object[] { field.getName(),
144: field.getType().getName(), fieldType.getName() };
145: throw new IllegalArgumentException(MessageFormat.format(
146: msg, args));
147: }
148:
149: return field;
150: }
151:
152: /**
153: * Returns the name of the field that this functor sets.
154: */
155: public String getFieldName() {
156: return _fieldName;
157: }
158:
159: // Binary interface
160:
161: /**
162: * Sets the designated field of the argument to the given value and returns
163: * null.
164: * <p>
165: * @return null
166: * @throws EvaluationException if the argument does not have the designated
167: * public field, or if it is accept the given value.
168: */
169: public R fn(T arg, R val) {
170: try {
171: getField(arg).set(arg, val);
172: return null;
173: } catch (ClassCastException x) {
174: String msg = "{0}.{1} is of type {2}";
175: Field f = getField(arg);
176: Object[] args = new Object[] { arg.getClass().getName(),
177: f.getName(), f.getType().getName() };
178: throw new EvaluationException(MessageFormat.format(msg,
179: args), x);
180: } catch (IllegalAccessException x) {
181: String msg = "{0}.{1} is not accessible";
182: Object[] args = new Object[] { arg.getClass().getName(),
183: getField(arg).getName() };
184: throw new EvaluationException(MessageFormat.format(msg,
185: args), x);
186: }
187: }
188:
189: private Field getField(T arg) {
190: if (_field == null) {
191: try {
192: _field = arg.getClass().getField(_fieldName);
193: } catch (NoSuchFieldException x) {
194: throw new EvaluationException(x);
195: }
196: }
197:
198: return _field;
199: }
200:
201: private String buildNoSuchFieldMessage(String fieldname, Class clasz) {
202: String msg = "class {0} does not have field {1}";
203: Object[] args = new Object[] { clasz.getName(), fieldname };
204: return MessageFormat.format(msg, args);
205: }
206:
207: /**
208: * Calls the Visitor's <code>visit(etField)</code> field, if it
209: * implements the nested Visitor interface.
210: */
211: public void accept(net.sf.jga.fn.Visitor v) {
212: if (v instanceof SetField.Visitor)
213: ((SetField.Visitor) v).visit(this );
214: else
215: v.visit(this );
216: }
217:
218: // Object overrides
219:
220: public String toString() {
221: return "SetField(" + _fieldName + ")";
222: }
223:
224: // AcyclicVisitor
225:
226: /**
227: * Interface for classes that may interpret a <b>SetField</b>
228: * function.
229: */
230: public interface Visitor extends net.sf.jga.fn.Visitor {
231: public void visit(SetField host);
232: }
233: }
|