public Value getRowValue(String collectionName, String objName,
String colName);
- public Random getRandom(Integer seed);
+ public float getRandom(Integer seed);
}
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
* MAX_CACHED_LOOKUP_TABLES).
*/
private final Map<String, TableInfo> _tableLookup =
- new LinkedHashMap<String, TableInfo>() {
- private static final long serialVersionUID = 0L;
- @Override
- protected boolean removeEldestEntry(Map.Entry<String, TableInfo> e) {
- return(size() > MAX_CACHED_LOOKUP_TABLES);
- }
- };
+ new SimpleCache<String,TableInfo>(MAX_CACHED_LOOKUP_TABLES);
/** set of table names as stored in the mdb file, created on demand */
private Set<String> _tableNames;
/** Reads and writes database pages */
--- /dev/null
+/*
+Copyright (c) 2018 James Ahlborn
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package com.healthmarketscience.jackcess.impl;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Simple LRU cache implementation which keeps at most the configured maximum
+ * number of elements.
+ * @author James Ahlborn
+ */
+public class SimpleCache<K,V> extends LinkedHashMap<K,V>
+{
+ private static final long serialVersionUID = 20180313L;
+
+ private final int _maxSize;
+
+ public SimpleCache(int maxSize) {
+ super(16, 0.75f, true);
+ _maxSize = maxSize;
+ }
+
+ protected int getMaxSize() {
+ return _maxSize;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<K, V> e) {
+ return(size() > _maxSize);
+ }
+}
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
Integer seed = ((params.length > 0) ? params[0].getAsLongInt() : null);
- return BuiltinOperators.toValue(ctx.getRandom(seed).nextFloat());
+ return BuiltinOperators.toValue(ctx.getRandom(seed));
}
});
--- /dev/null
+/*
+Copyright (c) 2018 James Ahlborn
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package com.healthmarketscience.jackcess.impl.expr;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * This class effectively encapsulates the stateful logic of the "Rnd"
+ * function.
+ *
+ * @author James Ahlborn
+ */
+public class RandomContext
+{
+ private Source _defRnd;
+ private Map<Integer,Source> _rnds;
+ // default to the value access uses for "last val" when none has been
+ // returned yet
+ private float _lastVal = 1.953125E-02f;
+
+ public RandomContext()
+ {
+ }
+
+ public float getRandom(Integer seed) {
+
+ if(seed == null) {
+ if(_defRnd == null) {
+ _defRnd = new SimpleSource(createRandom(System.currentTimeMillis()));
+ }
+ return _defRnd.get();
+ }
+
+ if(_rnds == null) {
+ // note, we don't use a SimpleCache here because if we discard a Random
+ // instance, that will cause the values to be reset
+ _rnds = new HashMap<Integer,Source>();
+ }
+
+ Source rnd = _rnds.get(seed);
+ if(rnd == null) {
+
+ int seedInt = seed;
+ if(seedInt > 0) {
+ // normal random with a user specified seed
+ rnd = new SimpleSource(createRandom(seedInt));
+ } else if(seedInt < 0) {
+ // returns the same value every time and resets all randoms
+ rnd = new ResetSource(createRandom(seedInt));
+ } else {
+ // returns the last random value returned
+ rnd = new LastValSource();
+ }
+
+ _rnds.put(seed, rnd);
+ }
+ return rnd.get();
+ }
+
+ private float setLast(float lastVal) {
+ _lastVal = lastVal;
+ return lastVal;
+ }
+
+ private void reset() {
+ if(_rnds != null) {
+ _rnds.clear();
+ }
+ }
+
+ private static Random createRandom(long seed) {
+ // FIXME, support SecureRandom?
+ return new Random(seed);
+ }
+
+ private abstract class Source
+ {
+ public float get() {
+ return setLast(getImpl());
+ }
+
+ protected abstract float getImpl();
+ }
+
+ private class SimpleSource extends Source
+ {
+ private final Random _rnd;
+
+ private SimpleSource(Random rnd) {
+ _rnd = rnd;
+ }
+
+ @Override
+ protected float getImpl() {
+ return _rnd.nextFloat();
+ }
+ }
+
+ private class ResetSource extends Source
+ {
+ private final float _val;
+
+ private ResetSource(Random rnd) {
+ _val = rnd.nextFloat();
+ }
+
+ @Override
+ protected float getImpl() {
+ reset();
+ return _val;
+ }
+ }
+
+ private class LastValSource extends Source
+ {
+ private LastValSource() {
+ }
+
+ @Override
+ protected float getImpl() {
+ return _lastVal;
+ }
+ }
+}
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
-import java.util.Random;
import com.healthmarketscience.jackcess.DatabaseBuilder;
import com.healthmarketscience.jackcess.TestUtil;
private static final class TestEvalContext implements EvalContext
{
private final Value _thisVal;
- private Random _defRnd;
- private Random _rnd;
- private long _rndSeed;
+ private final RandomContext _rndCtx = new RandomContext();
private TestEvalContext(Value thisVal) {
_thisVal = thisVal;
throw new UnsupportedOperationException();
}
- public Random getRandom(Integer seed) {
- if(seed == null) {
- if(_defRnd == null) {
- _defRnd = new Random(System.currentTimeMillis());
- }
- return _defRnd;
- }
- if((_rnd == null) || (seed != _rndSeed)) {
- _rndSeed = seed;
- _rnd = new Random(_rndSeed);
- }
- return _rnd;
+ public float getRandom(Integer seed) {
+ return _rndCtx.getRandom(seed);
}
}
}