001    /*
002     * Copyright 2011 Christian Kumpe http://kumpe.de/christian/java
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 de.kumpe.hadooptimizer;
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    
025    import org.apache.commons.math.random.RandomGenerator;
026    import org.apache.commons.math.random.Well44497b;
027    
028    /**
029     * The base class of all {@link Optimizer} configurations.
030     * <p>
031     * Subclasses have to override {@link #clone()} to cast the result to themself
032     * and {@link #validate()} to validate them.
033     * 
034     * @param <I>
035     *            the individuals' type
036     * 
037     * @author <a href="http://kumpe.de/christian/java">Christian Kumpe</a>
038     */
039    public abstract class OptimizerConfiguration<I> implements Serializable,
040                    Cloneable {
041            private static final long serialVersionUID = 1L;
042    
043            private final static class DefaultRandomGeneratorFactory implements
044                            RandomGeneratorFactory {
045                    private static final long serialVersionUID = 1L;
046    
047                    @Override
048                    public RandomGenerator createRandomGenerator(final long seed) {
049                            return new Well44497b(seed);
050                    }
051    
052                    @Override
053                    public int hashCode() {
054                            return 0;
055                    }
056    
057                    @Override
058                    public boolean equals(final Object obj) {
059                            if (null == obj) {
060                                    return false;
061                            }
062                            return getClass() == obj.getClass();
063                    }
064            }
065    
066            private PopulationReader<I> populationReader;
067            private PopulationWriter<I> populationWriter;
068            private RandomGeneratorFactory randomGeneratorFactory = new DefaultRandomGeneratorFactory();
069    
070            /**
071             * Returns a shallow copy of this configuration. This will not clone the
072             * configuration's components.
073             * <p>
074             * Subclasses should override this method to cast the result to themself.
075             * 
076             * @return the shallow copy of this configuration
077             */
078            @Override
079            @SuppressWarnings("unchecked")
080            public OptimizerConfiguration<I> clone() {
081                    try {
082                            return (OptimizerConfiguration<I>) super.clone();
083                    } catch (final CloneNotSupportedException e) {
084                            throw new AssertionError(); // Can't happen
085                    }
086            }
087    
088            /**
089             * @return the deep copy of the specified configuration
090             */
091            public static <I, T extends OptimizerConfiguration<I>> T deepClone(
092                            final T original) {
093                    try {
094                            // serialize
095                            final ByteArrayOutputStream bos = new ByteArrayOutputStream();
096                            final ObjectOutputStream oos = new ObjectOutputStream(bos);
097                            oos.writeObject(original);
098                            oos.close();
099    
100                            // deserialize
101                            final ByteArrayInputStream bis = new ByteArrayInputStream(
102                                            bos.toByteArray());
103                            final ObjectInputStream ois = new ObjectInputStream(bis);
104                            @SuppressWarnings("unchecked")
105                            final T clone = (T) ois.readObject();
106                            ois.close();
107    
108                            return clone;
109                    } catch (final IOException e) {
110                            throw new RuntimeException(e);
111                    } catch (final ClassNotFoundException e) {
112                            throw new RuntimeException(e);
113                    }
114            }
115    
116            /**
117             * Validates the configuration for completeness and correctness.
118             * <p>
119             * Subclasses have to override this method, then first call this method from
120             * the superclass and then do their own additional validation.
121             * <p>
122             * Verifies that a {@link #setPopulationReader(PopulationReader)
123             * populationReader} and {@link #setPopulationWriter(PopulationWriter)
124             * populationWriter} has been set.
125             * 
126             * @throws IllegalStateException
127             *             if this configuration is not valid
128             */
129            public void validate() {
130                    if (null == populationReader) {
131                            throw new IllegalStateException(
132                                            "A PopulatioReader has to be set in the configuration.");
133                    }
134                    if (null == populationWriter) {
135                            throw new IllegalStateException(
136                                            "A PopulationWriter has to be set in the configuration.");
137                    }
138            }
139    
140            /**
141             * Will inject the given {@link RandomGenerator} instance into all contained
142             * components which are implementing {@link NeedsRandomGenerator}.
143             * <p>
144             * Subclasses must override this method, if they contain additional
145             * components. Then first call this method from the superclass and then call
146             * {@link #injectRandomGenerator(Object, RandomGenerator)} for their
147             * additional components.
148             * <p>
149             * Calls {@link #injectRandomGenerator(Object, RandomGenerator)} on the
150             * given {@link #setPopulationReader(PopulationReader) populationReader} and
151             * {@link #setPopulationWriter(PopulationWriter) populationWriter}.
152             * 
153             * @param randomGenerator
154             *            the {@link RandomGenerator} instance to inject
155             */
156            public void injectRandomGenerator(final RandomGenerator randomGenerator) {
157                    injectRandomGenerator(populationReader, randomGenerator);
158                    injectRandomGenerator(populationWriter, randomGenerator);
159            }
160    
161            /**
162             * Inject the given {@link RandomGenerator} object into the passed
163             * {@code instance}, if {@code instance} is implementing
164             * {@link NeedsRandomGenerator}.
165             * 
166             * @param instance
167             *            the instance for injecting the {@code randomGenerator}, if
168             *            needed
169             * @param randomGenerator
170             *            the {@link RandomGenerator} object to inject
171             */
172            protected final void injectRandomGenerator(final Object instance,
173                            final RandomGenerator randomGenerator) {
174                    if (instance instanceof NeedsRandomGenerator) {
175                            ((NeedsRandomGenerator) instance)
176                                            .setRandomGenerator(randomGenerator);
177                    }
178            }
179    
180            /**
181             * @return the configured {@link PopulationReader}; <code>null</code> if
182             *         none has been set
183             */
184            public final PopulationReader<I> getPopulationReader() {
185                    return populationReader;
186            }
187    
188            /**
189             * @param populationReader
190             *            the {@link PopulationReader} for this configuration
191             * 
192             * @throws NullPointerException
193             *             if {@code populationReader} is <code>null</code>
194             */
195            public final void setPopulationReader(
196                            final PopulationReader<I> populationReader) {
197                    if (null == populationReader) {
198                            throw new NullPointerException("populationReader may not be null.");
199                    }
200    
201                    this.populationReader = populationReader;
202            }
203    
204            /**
205             * @return the configured {@link PopulationWriter}; <code>null</code> if
206             *         none has been set
207             */
208            public final PopulationWriter<I> getPopulationWriter() {
209                    return populationWriter;
210            }
211    
212            /**
213             * @param populationWriter
214             *            the {@link PopulationWriter} for this configuration
215             * 
216             * @throws NullPointerException
217             *             if {@code populationWriter} is <code>null</code>
218             */
219            public final void setPopulationWriter(
220                            final PopulationWriter<I> populationWriter) {
221                    if (null == populationWriter) {
222                            throw new NullPointerException("populationWriter may not be null.");
223                    }
224    
225                    this.populationWriter = populationWriter;
226            }
227    
228            /**
229             * @return the configured {@link RandomGeneratorFactory}; per default this
230             *         returns a factory which creates instances of {@link Well44497b}
231             */
232            public RandomGeneratorFactory getRandomGeneratorFactory() {
233                    assert null != randomGeneratorFactory;
234    
235                    return randomGeneratorFactory;
236            }
237    
238            /**
239             * @param randomGeneratorFactory
240             *            the {@link RandomGeneratorFactory} for this configuration
241             * 
242             * @throws NullPointerException
243             *             if {@code randomGeneratorFactory} is <code>null</code>
244             */
245            public void setRandomGeneratorFactory(
246                            final RandomGeneratorFactory randomGeneratorFactory) {
247                    if (null == randomGeneratorFactory) {
248                            throw new NullPointerException(
249                                            "randomGeneratorFactory may not be null.");
250                    }
251    
252                    this.randomGeneratorFactory = randomGeneratorFactory;
253            }
254    
255            @Override
256            public int hashCode() {
257                    assert null != randomGeneratorFactory;
258    
259                    final int prime = 31;
260                    int result = 1;
261                    result = prime * result
262                                    + (populationReader == null ? 0 : populationReader.hashCode());
263                    result = prime * result
264                                    + (populationWriter == null ? 0 : populationWriter.hashCode());
265                    result = prime * result + randomGeneratorFactory.hashCode();
266                    return result;
267            }
268    
269            @Override
270            public boolean equals(final Object obj) {
271                    assert null != randomGeneratorFactory;
272    
273                    if (this == obj) {
274                            return true;
275                    }
276                    if (obj == null) {
277                            return false;
278                    }
279                    if (!(obj instanceof OptimizerConfiguration)) {
280                            return false;
281                    }
282                    final OptimizerConfiguration<?> other = (OptimizerConfiguration<?>) obj;
283                    if (populationReader == null) {
284                            if (other.populationReader != null) {
285                                    return false;
286                            }
287                    } else if (!populationReader.equals(other.populationReader)) {
288                            return false;
289                    }
290                    if (populationWriter == null) {
291                            if (other.populationWriter != null) {
292                                    return false;
293                            }
294                    } else if (!populationWriter.equals(other.populationWriter)) {
295                            return false;
296                    }
297                    return randomGeneratorFactory.equals(other.randomGeneratorFactory);
298            }
299    }