001: // ============================================================================
002: // $Id: GetField.java,v 1.11 2006/08/05 21:34:32 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.EvaluationException;
039: import net.sf.jga.fn.UnaryFunctor;
040:
041: /**
042: * Unary Functor that returns the value of the named field for the argument.
043: * The field name and type are set at construction.
044: * <p>
045: * Copyright © 2002-2005 David A. Hall
046: *
047: * @author <a href="mailto:davidahall@users.sourceforge.net">David A. Hall</a>
048: **/
049:
050: public class GetField<T, R> extends UnaryFunctor<T, R> {
051:
052: static final long serialVersionUID = 5733484457689881481L;
053:
054: // The name of the field to be retrieved.
055: private String _fieldName;
056:
057: // The class from which the field is retrieved.
058: private Class<T> _argtype;
059:
060: // The Field whose valus is returned
061: private transient Field _field;
062:
063: /**
064: * Builds a GetField for the given field
065: * @throws IllegalArgumentException if the field is null, or is not a field
066: * of the given class
067: */
068: public GetField(Field field) {
069: if (field == null) {
070: throw new IllegalArgumentException("Must supply field");
071: }
072:
073: _field = field;
074: _fieldName = field.getName();
075: // @SuppressWarnings
076: // There's nothing we can do about this other than warn the users
077: // to make sure that they don't use an inappropriate argument type
078: _argtype = (Class<T>) field.getDeclaringClass();
079: }
080:
081: /**
082: * Builds a GetField for the given field
083: * @throws IllegalArgumentException if the field is null, or is not a field
084: * of the given class
085: */
086: public GetField(Class<T> argclass, Field field) {
087: if (field == null) {
088: throw new IllegalArgumentException("Must supply field");
089: }
090:
091: if (!field.getDeclaringClass().isAssignableFrom(argclass)) {
092: throw new IllegalArgumentException(buildNoSuchFieldMessage(
093: field.getName(), argclass));
094: }
095:
096: _field = field;
097: _fieldName = field.getName();
098: _argtype = argclass;
099: }
100:
101: /**
102: * Builds a GetField for the given field, using the given class to
103: * find the desired field.
104: * @throws IllegalArgumentException if the field name is null or empty,
105: *
106: */
107: public GetField(Class<T> argclass, String fieldName) {
108: if (fieldName == null || fieldName.length() == 0) {
109: throw new IllegalArgumentException("Must supply field name");
110: }
111:
112: _fieldName = fieldName;
113: _argtype = argclass;
114:
115: try {
116: _field = argclass.getField(_fieldName);
117: } catch (NoSuchFieldException x) {
118: IllegalArgumentException iax = new IllegalArgumentException(
119: buildNoSuchFieldMessage(fieldName, argclass));
120:
121: iax.initCause(x);
122: throw iax;
123: }
124: }
125:
126: /**
127: * Returns the class on which the field is retrieved.
128: */
129: // UNTESTED
130: public Class<T> getObjectType() {
131: return _argtype;
132: }
133:
134: /**
135: * Returns the name of the field that this functor retrieves.
136: */
137: public String getFieldName() {
138: return _fieldName;
139: }
140:
141: /**
142: * Returns the type of field that this functor retrieves.
143: */
144:
145: public Class<R> getFieldType() {
146: return (Class<R>) _field.getType();
147: }
148:
149: /**
150: * Lazily loads the Field object.
151: */
152:
153: // Instances of Reflection classes are not serializable, so this must be loaded lazily
154: // in case this object was read from a stream (the first time the functor is invoked,
155: // the field object will be instantiated)
156: public synchronized Field getField() throws NoSuchFieldException {
157: if (_field == null)
158: _field = _argtype.getField(_fieldName);
159:
160: return _field;
161: }
162:
163: // Unary interface
164:
165: /**
166: * Returns the value of the designated field of the argument
167: * @return the value of the designated field of the argument
168: * @throws EvaluationException if the argument does not have the designated
169: * public field, or if it is not of the correct type.
170: */
171: public R fn(T arg) {
172: try {
173: // @SuppressWarnings("unchecked")
174: // There's nothing we can do about this other than warn the users
175: // to make sure that they don't use an inappropriate return type
176: R val = (R) getField().get(arg);
177: return val;
178: } catch (NoSuchFieldException x) {
179: throw new EvaluationException(buildNoSuchFieldMessage(
180: _fieldName, _argtype));
181: } catch (ClassCastException x) {
182: String msg = "{0}.{1} is of type {2}";
183: Object[] args = new Object[] { arg.getClass().getName(),
184: _field.getName(), _field.getType().getName() };
185: throw new EvaluationException(MessageFormat.format(msg,
186: args), x);
187: } catch (IllegalAccessException x) {
188: String msg = "{0}.{1} is not accessible";
189: Object[] args = new Object[] { _argtype.getName(),
190: _fieldName };
191: throw new EvaluationException(MessageFormat.format(msg,
192: args), x);
193: }
194: }
195:
196: /**
197: */
198: private String buildNoSuchFieldMessage(String fieldname, Class clasz) {
199: String msg = "class {0} does not have field {1}";
200: Object[] args = new Object[] { clasz.getName(), fieldname };
201: return MessageFormat.format(msg, args);
202: }
203:
204: /**
205: * Calls the Visitor's <code>visit(GetField)</code> field, if it
206: * implements the nested Visitor interface.
207: */
208: public void accept(net.sf.jga.fn.Visitor v) {
209: if (v instanceof GetField.Visitor)
210: ((GetField.Visitor) v).visit(this );
211: else
212: v.visit(this );
213: }
214:
215: // Object overrides
216:
217: public String toString() {
218: return "GetField[" + _argtype.getName() + "." + _fieldName
219: + "]";
220: }
221:
222: // AcyclicVisitor
223:
224: /**
225: * Interface for classes that may interpret a <b>GetField</b>
226: * function.
227: */
228: public interface Visitor extends net.sf.jga.fn.Visitor {
229: public void visit(GetField host);
230: }
231: }
|