001: // ============================================================================
002: // $Id: Construct.java,v 1.11 2006/08/05 21:33:13 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.Constructor;
036: import java.lang.reflect.InvocationTargetException;
037: import java.text.MessageFormat;
038: import net.sf.jga.fn.EvaluationException;
039: import net.sf.jga.fn.UnaryFunctor;
040: import net.sf.jga.util.ArrayUtils;
041:
042: /**
043: * Unary Functor that constructs an object of the given class. The functor
044: * argument is an array containing the arguments to the class' constructor.
045: * <p>
046: * Copyright © 2003-2005 David A. Hall
047: *
048: * @author <a href="mailto:davidahall@users.sourceforge.net">David A. Hall</a>
049: **/
050:
051: public class Construct<R> extends UnaryFunctor<Object[], R> {
052:
053: static final long serialVersionUID = -4682718030847292345L;
054:
055: // the type of the object built by the constructor
056: private Class<R> _objclass;
057:
058: // the argument types for the constructor
059: private Class[] _argclasses;
060:
061: // the constructor being invoked
062: private transient Constructor<R> _ctor;
063:
064: /**
065: * Builds a functor that will build an object of class
066: * <code>ctorclass</code>, given arguments of classes in
067: * <code>argclasses</code>.
068: * @throws IllegalArgumentException if either argument is omitted, or if the
069: * constructor cannot be found.
070: */
071: public Construct(Class<R> ctorclass, Class... argclasses) {
072: this (argclasses, ctorclass);
073: }
074:
075: /**
076: * Builds a functor that will build an object of class
077: * <code>ctorclass</code>, given arguments of classes in
078: * <code>argclasses</code>.
079: * @throws IllegalArgumentException if either argument is omitted, or if the
080: * constructor cannot be found.
081: */
082: public Construct(Class[] argclasses, Class<R> ctorclass) {
083: if (argclasses == null) {
084: String msg = "Argument Classes must be specified";
085: throw new IllegalArgumentException(msg);
086: }
087:
088: if (ctorclass == null) {
089: String msg = "Class to be constructed must be specified";
090: throw new IllegalArgumentException(msg);
091: }
092:
093: _objclass = ctorclass;
094: _argclasses = argclasses;
095:
096: try {
097: _ctor = ctorclass.getConstructor(argclasses);
098: } catch (NoSuchMethodException x) {
099: String msg = "No constructor for class {0} takes argument(s) of type(s) {1}";
100: Object[] msgargs = new Object[] { _objclass.getName(),
101: ArrayUtils.toString(_argclasses) };
102: IllegalArgumentException x1 = new IllegalArgumentException(
103: MessageFormat.format(msg, msgargs));
104: x1.initCause(x);
105: throw x1;
106: }
107: }
108:
109: /**
110: * Builds a functor that will build an object using the given constructor.
111: * @throws IllegalArgumentException if the constructor is omitted.
112: */
113: public Construct(Constructor<R> ctor) {
114: if (ctor == null) {
115: String msg = "constructor must be specified";
116: throw new IllegalArgumentException(msg);
117: }
118:
119: _ctor = ctor;
120: _objclass = ctor.getDeclaringClass();
121: _argclasses = ctor.getParameterTypes();
122: }
123:
124: /**
125: * Lazy loads the constructor (used if the functor is called after
126: * deserialization)
127: */
128: public synchronized Constructor<R> getConstructor()
129: throws NoSuchMethodException {
130: if (_ctor == null)
131: _ctor = _objclass.getConstructor(_argclasses);
132:
133: return _ctor;
134: }
135:
136: // UnaryFunctor interface
137:
138: /**
139: * Builds an object via the designated constructor, passing the given
140: * array of argument values.
141: * <p>
142: * @return the object built by the constructor
143: */
144: public R fn(Object[] args) {
145: try {
146: return (R) getConstructor().newInstance(args);
147: } catch (NoSuchMethodException x) {
148: String msg = "No constructor for class {0} takes argument(s) of type(s) {1}";
149: Object[] msgargs = new Object[] { _objclass.getName(),
150: ArrayUtils.toString(_argclasses) };
151: throw new EvaluationException(MessageFormat.format(msg,
152: msgargs), x);
153: } catch (InstantiationException x) {
154: String msg = "class {0} is abstract: cannot be constructed";
155: Object[] msgargs = new Object[] { _objclass.getName() };
156: throw new EvaluationException(MessageFormat.format(msg,
157: msgargs), x);
158: } catch (IllegalAccessException x) {
159: String msg = "class {0} ctor({1}) is not accessible";
160: Object[] msgargs = new Object[] { _objclass.getName(),
161: ArrayUtils.toString(_argclasses) };
162: throw new EvaluationException(MessageFormat.format(msg,
163: msgargs), x);
164: } catch (InvocationTargetException x) {
165: String msg = "class {0} ctor({1}) failed: "
166: + x.getMessage();
167: Object[] msgargs = new Object[] { _objclass.getName(),
168: ArrayUtils.toString(_argclasses) };
169: throw new EvaluationException(MessageFormat.format(msg,
170: msgargs), x);
171: } catch (ClassCastException x) {
172: String msg = "ClassCastException: " + _objclass + "("
173: + ArrayUtils.toString(args) + ")";
174: throw new EvaluationException(msg, x);
175: } catch (IllegalArgumentException x) {
176: String msg = "class {0} ctor({1}) cannot be called with {2}";
177: Object[] msgargs = new Object[] { _objclass.getName(),
178: ArrayUtils.toString(_argclasses),
179: ArrayUtils.toString(args) };
180: throw new EvaluationException(MessageFormat.format(msg,
181: msgargs), x);
182: }
183: }
184:
185: /**
186: * Calls the Visitor's <code>visit(Construct)</code> method, if it
187: * implements the nested Visitor interface.
188: */
189: public void accept(net.sf.jga.fn.Visitor v) {
190: if (v instanceof Construct.Visitor)
191: ((Construct.Visitor) v).visit(this );
192: else
193: v.visit(this );
194: }
195:
196: // Object overrides
197:
198: public String toString() {
199: return "Construct(" + _objclass.getName() + ".getConstructor("
200: + ArrayUtils.toString(_argclasses) + "))";
201: }
202:
203: // Acyclic Visitor
204:
205: /**
206: * Interface for classes that may interpret a <b>Construct</b>
207: * predicate.
208: */
209: public interface Visitor extends net.sf.jga.fn.Visitor {
210: public void visit(Construct host);
211: }
212: }
|