001: /*
002: * Copyright 2001-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.collections.functors;
017:
018: import java.io.ByteArrayInputStream;
019: import java.io.ByteArrayOutputStream;
020: import java.io.IOException;
021: import java.io.ObjectInputStream;
022: import java.io.ObjectOutputStream;
023: import java.io.Serializable;
024: import java.lang.reflect.InvocationTargetException;
025: import java.lang.reflect.Method;
026:
027: import org.apache.commons.collections.Factory;
028: import org.apache.commons.collections.FunctorException;
029:
030: /**
031: * Factory implementation that creates a new instance each time based on a prototype.
032: *
033: * @since Commons Collections 3.0
034: * @version $Revision: 348444 $ $Date: 2005-11-23 14:06:56 +0000 (Wed, 23 Nov 2005) $
035: *
036: * @author Stephen Colebourne
037: */
038: public class PrototypeFactory {
039:
040: /**
041: * Factory method that performs validation.
042: * <p>
043: * Creates a Factory that will return a clone of the same prototype object
044: * each time the factory is used. The prototype will be cloned using one of these
045: * techniques (in order):
046: * <ul>
047: * <li>public clone method
048: * <li>public copy constructor
049: * <li>serialization clone
050: * <ul>
051: *
052: * @param prototype the object to clone each time in the factory
053: * @return the <code>prototype</code> factory
054: * @throws IllegalArgumentException if the prototype is null
055: * @throws IllegalArgumentException if the prototype cannot be cloned
056: */
057: public static Factory getInstance(Object prototype) {
058: if (prototype == null) {
059: return ConstantFactory.NULL_INSTANCE;
060: }
061: try {
062: Method method = prototype.getClass().getMethod("clone",
063: (Class[]) null);
064: return new PrototypeCloneFactory(prototype, method);
065:
066: } catch (NoSuchMethodException ex) {
067: try {
068: prototype.getClass().getConstructor(
069: new Class[] { prototype.getClass() });
070: return new InstantiateFactory(prototype.getClass(),
071: new Class[] { prototype.getClass() },
072: new Object[] { prototype });
073:
074: } catch (NoSuchMethodException ex2) {
075: if (prototype instanceof Serializable) {
076: return new PrototypeSerializationFactory(
077: (Serializable) prototype);
078: }
079: }
080: }
081: throw new IllegalArgumentException(
082: "The prototype must be cloneable via a public clone method");
083: }
084:
085: /**
086: * Constructor that performs no validation.
087: * Use <code>getInstance</code> if you want that.
088: */
089: private PrototypeFactory() {
090: super ();
091: }
092:
093: // PrototypeCloneFactory
094: //-----------------------------------------------------------------------
095: /**
096: * PrototypeCloneFactory creates objects by copying a prototype using the clone method.
097: */
098: static class PrototypeCloneFactory implements Factory, Serializable {
099:
100: /** The serial version */
101: private static final long serialVersionUID = 5604271422565175555L;
102:
103: /** The object to clone each time */
104: private final Object iPrototype;
105: /** The method used to clone */
106: private transient Method iCloneMethod;
107:
108: /**
109: * Constructor to store prototype.
110: */
111: private PrototypeCloneFactory(Object prototype, Method method) {
112: super ();
113: iPrototype = prototype;
114: iCloneMethod = method;
115: }
116:
117: /**
118: * Find the Clone method for the class specified.
119: */
120: private void findCloneMethod() {
121: try {
122: iCloneMethod = iPrototype.getClass().getMethod("clone",
123: (Class[]) null);
124:
125: } catch (NoSuchMethodException ex) {
126: throw new IllegalArgumentException(
127: "PrototypeCloneFactory: The clone method must exist and be public ");
128: }
129: }
130:
131: /**
132: * Creates an object by calling the clone method.
133: *
134: * @return the new object
135: */
136: public Object create() {
137: // needed for post-serialization
138: if (iCloneMethod == null) {
139: findCloneMethod();
140: }
141:
142: try {
143: return iCloneMethod.invoke(iPrototype, (Object[]) null);
144:
145: } catch (IllegalAccessException ex) {
146: throw new FunctorException(
147: "PrototypeCloneFactory: Clone method must be public",
148: ex);
149: } catch (InvocationTargetException ex) {
150: throw new FunctorException(
151: "PrototypeCloneFactory: Clone method threw an exception",
152: ex);
153: }
154: }
155: }
156:
157: // PrototypeSerializationFactory
158: //-----------------------------------------------------------------------
159: /**
160: * PrototypeSerializationFactory creates objects by cloning a prototype using serialization.
161: */
162: static class PrototypeSerializationFactory implements Factory,
163: Serializable {
164:
165: /** The serial version */
166: private static final long serialVersionUID = -8704966966139178833L;
167:
168: /** The object to clone via serialization each time */
169: private final Serializable iPrototype;
170:
171: /**
172: * Constructor to store prototype
173: */
174: private PrototypeSerializationFactory(Serializable prototype) {
175: super ();
176: iPrototype = prototype;
177: }
178:
179: /**
180: * Creates an object using serialization.
181: *
182: * @return the new object
183: */
184: public Object create() {
185: ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
186: ByteArrayInputStream bais = null;
187: try {
188: ObjectOutputStream out = new ObjectOutputStream(baos);
189: out.writeObject(iPrototype);
190:
191: bais = new ByteArrayInputStream(baos.toByteArray());
192: ObjectInputStream in = new ObjectInputStream(bais);
193: return in.readObject();
194:
195: } catch (ClassNotFoundException ex) {
196: throw new FunctorException(ex);
197: } catch (IOException ex) {
198: throw new FunctorException(ex);
199: } finally {
200: try {
201: if (bais != null) {
202: bais.close();
203: }
204: } catch (IOException ex) {
205: // ignore
206: }
207: try {
208: if (baos != null) {
209: baos.close();
210: }
211: } catch (IOException ex) {
212: // ignore
213: }
214: }
215: }
216: }
217:
218: }
|