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