git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/exprs@1144 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-2.2.0
@@ -36,5 +36,5 @@ public interface EvalContext | |||
public Value getRowValue(String collectionName, String objName, | |||
String colName); | |||
public Random getRandom(Integer seed); | |||
public float getRandom(Integer seed); | |||
} |
@@ -39,7 +39,6 @@ import java.util.EnumMap; | |||
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; | |||
@@ -266,13 +265,7 @@ public class DatabaseImpl implements Database | |||
* 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 */ |
@@ -0,0 +1,46 @@ | |||
/* | |||
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); | |||
} | |||
} |
@@ -117,7 +117,7 @@ public class DefaultNumberFunctions | |||
@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)); | |||
} | |||
}); | |||
@@ -0,0 +1,140 @@ | |||
/* | |||
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; | |||
} | |||
} | |||
} |
@@ -19,7 +19,6 @@ package com.healthmarketscience.jackcess.impl.expr; | |||
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; | |||
@@ -382,9 +381,7 @@ public class ExpressionatorTest extends TestCase | |||
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; | |||
@@ -416,18 +413,8 @@ public class ExpressionatorTest extends TestCase | |||
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); | |||
} | |||
} | |||
} |