View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.rng.simple;
18  
19  import java.io.IOException;
20  import java.io.ObjectOutputStream;
21  import java.io.ObjectInputStream;
22  import java.util.Random;
23  import org.apache.commons.rng.RestorableUniformRandomProvider;
24  import org.apache.commons.rng.core.RandomProviderDefaultState;
25  
26  /**
27   * Subclass of {@link Random} that {@link #next(int) delegates} to a
28   * {@link RestorableUniformRandomProvider} instance but will otherwise rely
29   * on the base class for generating all the random types.
30   *
31   * <p>
32   * Legacy applications coded against the JDK's API could use this subclass
33   * of {@link Random} in order to replace its linear congruential generator
34   * by any {@link RandomSource}.
35   * </p>
36   *
37   * Caveat: Use of this class is <em>not</em> recommended for new applications.
38   * In particular, there is no guarantee that the serialized form of this class
39   * will be compatible across (even <em>minor</em>) releases of the library.
40   *
41   * @since 1.0
42   */
43  public final class JDKRandomBridge extends Random {
44      /** Serializable version identifier. */
45      private static final long serialVersionUID = 20161107L;
46      /** Source. */
47      private final RandomSource source;
48      /** Delegate. */
49      private transient RestorableUniformRandomProvider delegate;
50      /** Workaround JDK's "Random" bug: https://bugs.openjdk.java.net/browse/JDK-8154225 */
51      private final transient boolean isInitialized;
52  
53      /**
54       * Creates a new instance.
55       *
56       * @param source Source of randomness.
57       * @param seed Seed.  Can be {@code null}.
58       */
59      public JDKRandomBridge(RandomSource source,
60                             Object seed) {
61          this.source = source;
62          delegate = RandomSource.create(source, seed);
63          isInitialized = true;
64      }
65  
66      /** {@inheritDoc} */
67      @Override
68      public synchronized void setSeed(long seed) {
69          if (isInitialized) {
70              delegate = RandomSource.create(source, seed);
71  
72              // Force the clearing of the "haveNextNextGaussian" flag
73              // (cf. Javadoc of the base class); the value passed here
74              // is irrelevant (since it will not be used).
75              super.setSeed(0L);
76          }
77      }
78  
79      /**
80       * Delegates the generation of 32 random bits to the
81       * {@code RandomSource} argument provided at
82       * {@link #JDKRandomBridge(RandomSource,Object) construction}.
83       * The returned value is such that if the source of randomness is
84       * {@link RandomSource#JDK}, all the generated values will be identical
85       * to those produced by the same sequence of calls on a {@link Random}
86       * instance initialized with the same seed.
87       *
88       * @param n Number of random bits which the requested value must contain.
89       * @return the value represented by the {@code n} high-order bits of a
90       * pseudo-random 32-bits integer.
91       */
92      @Override
93      protected synchronized int next(int n) {
94          return delegate.nextInt() >>> (32 - n);
95      }
96  
97      /**
98       * @param out Output stream.
99       * @throws IOException if an error occurs.
100      */
101     private synchronized void writeObject(ObjectOutputStream out)
102         throws IOException {
103         // Write non-transient fields.
104         out.defaultWriteObject();
105 
106         // Save current state.
107         out.writeObject(((RandomProviderDefaultState) delegate.saveState()).getState());
108    }
109 
110     /**
111      * @param in Input stream.
112      * @throws IOException if an error occurs.
113      * @throws ClassNotFoundException if an error occurs.
114      */
115     private void readObject(ObjectInputStream in)
116         throws IOException,
117                ClassNotFoundException {
118         // Read non-transient fields.
119         in.defaultReadObject();
120 
121         // Recreate the "delegate" from serialized info.
122         delegate = RandomSource.create(source);
123         // And restore its state.
124         delegate.restoreState(new RandomProviderDefaultState((byte[]) in.readObject()));
125     }
126 }