1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.collections.functors;
  17. import java.io.ByteArrayInputStream;
  18. import java.io.ByteArrayOutputStream;
  19. import java.io.IOException;
  20. import java.io.ObjectInputStream;
  21. import java.io.ObjectOutputStream;
  22. import java.io.Serializable;
  23. import java.lang.reflect.InvocationTargetException;
  24. import java.lang.reflect.Method;
  25. import org.apache.commons.collections.Factory;
  26. import org.apache.commons.collections.FunctorException;
  27. /**
  28. * Factory implementation that creates a new instance each time based on a prototype.
  29. *
  30. * @since Commons Collections 3.0
  31. * @version $Revision: 1.7 $ $Date: 2004/05/16 11:47:38 $
  32. *
  33. * @author Stephen Colebourne
  34. */
  35. public class PrototypeFactory {
  36. /**
  37. * Factory method that performs validation.
  38. * <p>
  39. * Creates a Factory that will return a clone of the same prototype object
  40. * each time the factory is used. The prototype will be cloned using one of these
  41. * techniques (in order):
  42. * <ul>
  43. * <li>public clone method
  44. * <li>public copy constructor
  45. * <li>serialization clone
  46. * <ul>
  47. *
  48. * @param prototype the object to clone each time in the factory
  49. * @return the <code>prototype</code> factory
  50. * @throws IllegalArgumentException if the prototype is null
  51. * @throws IllegalArgumentException if the prototype cannot be cloned
  52. */
  53. public static Factory getInstance(Object prototype) {
  54. if (prototype == null) {
  55. return ConstantFactory.NULL_INSTANCE;
  56. }
  57. try {
  58. Method method = prototype.getClass().getMethod("clone", null);
  59. return new PrototypeCloneFactory(prototype, method);
  60. } catch (NoSuchMethodException ex) {
  61. try {
  62. prototype.getClass().getConstructor(new Class[] { prototype.getClass()});
  63. return new InstantiateFactory(
  64. prototype.getClass(),
  65. new Class[] { prototype.getClass()},
  66. new Object[] { prototype });
  67. } catch (NoSuchMethodException ex2) {
  68. if (prototype instanceof Serializable) {
  69. return new PrototypeSerializationFactory((Serializable) prototype);
  70. }
  71. }
  72. }
  73. throw new IllegalArgumentException("The prototype must be cloneable via a public clone method");
  74. }
  75. /**
  76. * Constructor that performs no validation.
  77. * Use <code>getInstance</code> if you want that.
  78. */
  79. private PrototypeFactory() {
  80. super();
  81. }
  82. // PrototypeCloneFactory
  83. //-----------------------------------------------------------------------
  84. /**
  85. * PrototypeCloneFactory creates objects by copying a prototype using the clone method.
  86. */
  87. static class PrototypeCloneFactory implements Factory, Serializable {
  88. /** The serial version */
  89. static final long serialVersionUID = 5604271422565175555L;
  90. /** The object to clone each time */
  91. private final Object iPrototype;
  92. /** The method used to clone */
  93. private transient Method iCloneMethod;
  94. /**
  95. * Constructor to store prototype.
  96. */
  97. private PrototypeCloneFactory(Object prototype, Method method) {
  98. super();
  99. iPrototype = prototype;
  100. iCloneMethod = method;
  101. }
  102. /**
  103. * Find the Clone method for the class specified.
  104. */
  105. private void findCloneMethod() {
  106. try {
  107. iCloneMethod = iPrototype.getClass().getMethod("clone", null);
  108. } catch (NoSuchMethodException ex) {
  109. throw new IllegalArgumentException("PrototypeCloneFactory: The clone method must exist and be public ");
  110. }
  111. }
  112. /**
  113. * Creates an object by calling the clone method.
  114. *
  115. * @return the new object
  116. */
  117. public Object create() {
  118. // needed for post-serialization
  119. if (iCloneMethod == null) {
  120. findCloneMethod();
  121. }
  122. try {
  123. return iCloneMethod.invoke(iPrototype, null);
  124. } catch (IllegalAccessException ex) {
  125. throw new FunctorException("PrototypeCloneFactory: Clone method must be public", ex);
  126. } catch (InvocationTargetException ex) {
  127. throw new FunctorException("PrototypeCloneFactory: Clone method threw an exception", ex);
  128. }
  129. }
  130. }
  131. // PrototypeSerializationFactory
  132. //-----------------------------------------------------------------------
  133. /**
  134. * PrototypeSerializationFactory creates objects by cloning a prototype using serialization.
  135. */
  136. static class PrototypeSerializationFactory implements Factory, Serializable {
  137. /** The serial version */
  138. static final long serialVersionUID = -8704966966139178833L;
  139. /** The object to clone via serialization each time */
  140. private final Serializable iPrototype;
  141. /**
  142. * Constructor to store prototype
  143. */
  144. private PrototypeSerializationFactory(Serializable prototype) {
  145. super();
  146. iPrototype = prototype;
  147. }
  148. /**
  149. * Creates an object using serialization.
  150. *
  151. * @return the new object
  152. */
  153. public Object create() {
  154. ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
  155. ByteArrayInputStream bais = null;
  156. try {
  157. ObjectOutputStream out = new ObjectOutputStream(baos);
  158. out.writeObject(iPrototype);
  159. bais = new ByteArrayInputStream(baos.toByteArray());
  160. ObjectInputStream in = new ObjectInputStream(bais);
  161. return in.readObject();
  162. } catch (ClassNotFoundException ex) {
  163. throw new FunctorException(ex);
  164. } catch (IOException ex) {
  165. throw new FunctorException(ex);
  166. } finally {
  167. try {
  168. if (bais != null) {
  169. bais.close();
  170. }
  171. } catch (IOException ex) {
  172. // ignore
  173. }
  174. try {
  175. if (baos != null) {
  176. baos.close();
  177. }
  178. } catch (IOException ex) {
  179. // ignore
  180. }
  181. }
  182. }
  183. }
  184. }