summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernard Normier <bernard@zeroc.com>2005-11-11 19:37:06 +0000
committerBernard Normier <bernard@zeroc.com>2005-11-11 19:37:06 +0000
commit0fd5ba1503be1db3377cc856ec79ed4902a062db (patch)
tree8305898e309a3b91392dbefad8f70ca0d38eaeb1
parentupdate PYTHONPATH paths (diff)
downloadice-0fd5ba1503be1db3377cc856ec79ed4902a062db.tar.bz2
ice-0fd5ba1503be1db3377cc856ec79ed4902a062db.tar.xz
ice-0fd5ba1503be1db3377cc856ec79ed4902a062db.zip
Freeze Map improvements (bug #415)
-rw-r--r--cpp/include/Freeze/Map.h1
-rw-r--r--cpp/src/slice2freezej/Main.cpp511
-rw-r--r--cpp/test/Freeze/dbmap/Client.cpp4
-rw-r--r--java/CHANGES24
-rwxr-xr-xjava/src/Freeze/KeyCodec.java16
-rw-r--r--java/src/Freeze/Map.java768
-rwxr-xr-xjava/src/Freeze/SharedDb.java25
-rwxr-xr-xjava/src/Freeze/SubMap.java476
-rw-r--r--java/test/Freeze/dbmap/Client.java157
-rw-r--r--java/test/Freeze/dbmap/build.xml8
10 files changed, 1695 insertions, 295 deletions
diff --git a/cpp/include/Freeze/Map.h b/cpp/include/Freeze/Map.h
index 365e2a03af2..d70c2106d3f 100644
--- a/cpp/include/Freeze/Map.h
+++ b/cpp/include/Freeze/Map.h
@@ -80,7 +80,6 @@ private:
std::string _name;
MapIndexI* _impl;
const MapHelperI* _map;
- const KeyCompareBasePtr _keyCompare;
};
typedef IceUtil::Handle<MapIndexBase> MapIndexBasePtr;
diff --git a/cpp/src/slice2freezej/Main.cpp b/cpp/src/slice2freezej/Main.cpp
index fe2eb59d16a..cd2144ee828 100644
--- a/cpp/src/slice2freezej/Main.cpp
+++ b/cpp/src/slice2freezej/Main.cpp
@@ -53,6 +53,9 @@ public:
bool generate(UnitPtr&, const Index&);
private:
+ string varToObject(const TypePtr&, const string&);
+ string objectToVar(const TypePtr&, const string&);
+
string _prog;
};
@@ -62,6 +65,106 @@ FreezeGenerator::FreezeGenerator(const string& prog, const string& dir)
{
}
+string
+FreezeGenerator::varToObject(const TypePtr& type, const string& param)
+{
+ string result = param;
+
+ BuiltinPtr b = BuiltinPtr::dynamicCast(type);
+ if(b != 0)
+ {
+ switch(b->kind())
+ {
+ case Builtin::KindByte:
+ {
+ result = string("new java.lang.Byte(") + param + ")";
+ break;
+ }
+ case Builtin::KindBool:
+ {
+ result = string("new java.lang.Boolean(") + param + ")";
+ break;
+ }
+ case Builtin::KindShort:
+ {
+ result = string("new java.lang.Short(") + param + ")";
+ break;
+ }
+ case Builtin::KindInt:
+ {
+ result = string("new java.lang.Integer(") + param + ")";
+ break;
+ }
+ case Builtin::KindLong:
+ {
+ result = string("new java.lang.Long(") + param + ")";
+ break;
+ }
+ case Builtin::KindFloat:
+ {
+ result = string("new java.lang.Float(") + param + ")";
+ break;
+ }
+ case Builtin::KindDouble:
+ {
+ result = string("new java.lang.Double(") + param + ")";
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+string
+FreezeGenerator::objectToVar(const TypePtr& type, const string& param)
+{
+ string result = string("((") + typeToString(type, TypeModeIn) + ")" + param + ")";
+
+ BuiltinPtr b = BuiltinPtr::dynamicCast(type);
+ if(b != 0)
+ {
+ switch(b->kind())
+ {
+ case Builtin::KindByte:
+ {
+ result = string("((java.lang.Byte)") + param + ").byteValue()";
+ break;
+ }
+ case Builtin::KindBool:
+ {
+ result = string("((java.lang.Boolean)") + param + ").booleanValue()";
+ break;
+ }
+ case Builtin::KindShort:
+ {
+ result = string("((java.lang.Short)") + param + ").shortValue()";
+ break;
+ }
+ case Builtin::KindInt:
+ {
+ result = string("((java.lang.Integer)") + param + ").intValue()";
+ break;
+ }
+ case Builtin::KindLong:
+ {
+ result = string("((java.lang.Long)") + param + ").longValue()";
+ break;
+ }
+ case Builtin::KindFloat:
+ {
+ result = string("((java.lang.Float)") + param + ").floatValue()";
+ break;
+ }
+ case Builtin::KindDouble:
+ {
+ result = string("((java.lang.Double)") + param + ").doubleValue()";
+ break;
+ }
+ }
+ }
+ return result;
+}
+
bool
FreezeGenerator::generate(UnitPtr& u, const Dict& dict)
{
@@ -231,20 +334,16 @@ FreezeGenerator::generate(UnitPtr& u, const Dict& dict)
out << sb;
//
- // Constructor
+ // Constructors
//
- out << sp << nl << "public" << nl << name
- << "(Freeze.Connection __connection, String __dbName, boolean __createDb)";
- out << sb;
-
- if(dict.indices.size() == 0)
- {
- out << nl << "super(__connection, __dbName, \"" << keyType->typeId() << "\", \""
- << valueType->typeId() << "\", __createDb);";
- }
- else
+ if(dict.indices.size() > 0)
{
- out << nl << "super(__connection, __dbName);";
+ out << sp << nl << "public" << nl << name
+ << "(Freeze.Connection __connection, String __dbName, boolean __createDb, "
+ << "java.util.Comparator __comparator, java.util.Map __indexComparators)";
+ out << sb;
+
+ out << nl << "super(__connection, __dbName, __comparator);";
out << nl << "_indices = new Freeze.Map.Index[" << dict.indices.size() << "];";
for(i = 0; i < dict.indices.size(); ++i)
{
@@ -252,28 +351,60 @@ FreezeGenerator::generate(UnitPtr& u, const Dict& dict)
<< "Index(\"" << indexNames[i] << "\");";
}
out << nl << "init(_indices, __dbName, \"" << keyType->typeId() << "\", \""
- << valueType->typeId() << "\", __createDb);";
+ << valueType->typeId() << "\", __createDb, __indexComparators);";
+ out << eb;
}
+
+ out << sp << nl << "public" << nl << name
+ << "(Freeze.Connection __connection, String __dbName, boolean __createDb, "
+ << "java.util.Comparator __comparator)";
+ out << sb;
+ if(dict.indices.size() > 0)
+ {
+ out << nl << "this(__connection, __dbName, __createDb, __comparator, null);";
+ }
+ else
+ {
+ out << nl << "super(__connection, __dbName, \"" << keyType->typeId() << "\", \""
+ << valueType->typeId() << "\", __createDb, __comparator);";
+ }
+ out << eb;
+
+ out << sp << nl << "public" << nl << name
+ << "(Freeze.Connection __connection, String __dbName, boolean __createDb)";
+ out << sb;
+ out << nl << "this(__connection, __dbName, __createDb, null);";
+ out << eb;
+
+ out << sp << nl << "public" << nl << name
+ << "(Freeze.Connection __connection, String __dbName)";
+ out << sb;
+ out << nl << "this(__connection, __dbName, true);";
out << eb;
//
- // findBy, count and encode methods
+ // findBy and count methods
//
-
for(i = 0; i < capitalizedMembers.size(); ++i)
{
string indexClassName = capitalizedMembers[i] + "Index";
out << sp << nl << "public Freeze.Map.EntryIterator";
out << nl << "findBy" << capitalizedMembers[i] << "("
+ << typeToString(indexTypes[i], TypeModeIn) << " __index, boolean __onlyDups)";
+ out << sb;
+ out << nl << "return _indices[" << i << "].untypedFind("
+ << varToObject(indexTypes[i], "__index") << ", __onlyDups);";
+ out << eb;
+
+ out << sp << nl << "public Freeze.Map.EntryIterator";
+ out << nl << "findBy" << capitalizedMembers[i] << "("
<< typeToString(indexTypes[i], TypeModeIn) << " __index)";
out << sb;
out << nl << "return _indices[" << i << "].untypedFind("
- << "encode" << indexClassName
- << "(__index, ((Freeze.Connection)_connection).getCommunicator()));";
+ << varToObject(indexTypes[i], "__index") << ", true);";
out << eb;
-
string countMethod = dict.indices[i].member.empty() ?
"valueCount" : dict.indices[i].member + "Count";
out << sp << nl << "public int";
@@ -281,90 +412,12 @@ FreezeGenerator::generate(UnitPtr& u, const Dict& dict)
<< typeToString(indexTypes[i], TypeModeIn) << " __index)";
out << sb;
out << nl << "return _indices[" << i << "].untypedCount("
- << "encode" << indexClassName
- << "(__index, ((Freeze.Connection)_connection).getCommunicator()));";
- out << eb;
-
-
- out << sp << nl << "private byte[]";
- out << nl << "encode" << indexClassName << "(" << typeToString(indexTypes[i], TypeModeIn)
- << " __key, Ice.Communicator __communicator)";
- out << sb;
- if(dict.indices[i].member.empty() && dict.indices[i].caseSensitive)
- {
- string param;
- BuiltinPtr b = BuiltinPtr::dynamicCast(indexTypes[i]);
- if(b != 0)
- {
- switch(b->kind())
- {
- case Builtin::KindByte:
- {
- param = "new java.lang.Byte(__key)";
- break;
- }
- case Builtin::KindBool:
- {
- param = "new java.lang.Boolean(__key)";
- break;
- }
- case Builtin::KindShort:
- {
- param = "new java.lang.Short(__key)";
- break;
- }
- case Builtin::KindInt:
- {
- param = "new java.lang.Integer(__key)";
- break;
- }
- case Builtin::KindLong:
- {
- param = "new java.lang.Long(__key)";
- break;
- }
- case Builtin::KindFloat:
- {
- param = "new java.lang.Float(__key)";
- break;
- }
- case Builtin::KindDouble:
- {
- param = "new java.lang.Double(__key)";
- break;
- }
- default:
- {
- param = "__key";
- break;
- }
- }
- }
-
- out << nl << "return encodeValue(" << param << ", __communicator);";
- }
- else
- {
- string valueS = dict.indices[i].caseSensitive ? "__key" : "__key.toLowerCase()";
-
- out << nl << "IceInternal.BasicStream __os = "
- << "new IceInternal.BasicStream(Ice.Util.getInstance(__communicator));";
- int iter = 0;
- writeMarshalUnmarshalCode(out, "", indexTypes[i], valueS, true, iter, false);
- if(indexTypes[i]->usesClasses())
- {
- out << nl << "__os.writePendingObjects();";
- }
- out << nl << "java.nio.ByteBuffer __buf = __os.prepareWrite();";
- out << nl << "byte[] __r = new byte[__buf.limit()];";
- out << nl << "__buf.get(__r);";
- out << nl << "return __r;";
- }
+ << varToObject(indexTypes[i], "__index") << ");";
out << eb;
}
//
- // encode/decode
+ // Top-level encode/decode
//
for(i = 0; i < 2; i++)
{
@@ -388,63 +441,18 @@ FreezeGenerator::generate(UnitPtr& u, const Dict& dict)
encaps = true;
}
- string typeS, valS;
+ string valS = objectToVar(type, "o");
+ string typeS;
+
BuiltinPtr b = BuiltinPtr::dynamicCast(type);
- if(b)
+ if(b != 0)
{
typeS = builtinTable[b->kind()];
- switch(b->kind())
- {
- case Builtin::KindByte:
- {
- valS = "((java.lang.Byte)o).byteValue()";
- break;
- }
- case Builtin::KindBool:
- {
- valS = "((java.lang.Boolean)o).booleanValue()";
- break;
- }
- case Builtin::KindShort:
- {
- valS = "((java.lang.Short)o).shortValue()";
- break;
- }
- case Builtin::KindInt:
- {
- valS = "((java.lang.Integer)o).intValue()";
- break;
- }
- case Builtin::KindLong:
- {
- valS = "((java.lang.Long)o).longValue()";
- break;
- }
- case Builtin::KindFloat:
- {
- valS = "((java.lang.Float)o).floatValue()";
- break;
- }
- case Builtin::KindDouble:
- {
- valS = "((java.lang.Double)o).doubleValue()";
- break;
- }
- case Builtin::KindString:
- case Builtin::KindObject:
- case Builtin::KindObjectProxy:
- case Builtin::KindLocalObject:
- {
- valS = "((" + typeS + ")o)";
- break;
- }
- }
- }
- else
- {
- typeS = typeToString(type, TypeModeIn);
- valS = "((" + typeS + ")o)";
- }
+ }
+ else
+ {
+ typeS = typeToString(type, TypeModeIn);
+ }
int iter;
@@ -591,28 +599,203 @@ FreezeGenerator::generate(UnitPtr& u, const Dict& dict)
out << sp << nl << "private class " << indexClassName << " extends Freeze.Map.Index";
out << sb;
- out << sp << nl << "protected byte[]";
- out << nl << "marshalKey(byte[] __value)";
+ //
+ // encodeKey
+ //
+ out << sp << nl << "public byte[]";
+ out << nl << "encodeKey(Object key, Ice.Communicator communicator)";
out << sb;
- if(dict.indices[i].member.empty() && dict.indices[i].caseSensitive)
+ if(dict.indices[i].member.empty())
{
- out << nl << "return __value;";
+ //
+ // Encode the full value (with an encaps!)
+ //
+ string keyS = "key";
+ if(!dict.indices[i].caseSensitive)
+ {
+ keyS = "((String)key).toLowerCase()";
+ }
+
+ out << nl << "return encodeValue(" << keyS << ", communicator);";
+ }
+ else
+ {
+ //
+ // No encaps
+ //
+ string keyS = dict.indices[i].caseSensitive ?
+ "key" : "((String)key).toLowerCase()";
+
+ keyS = objectToVar(indexTypes[i], keyS);
+
+ out << nl << "IceInternal.BasicStream __os = "
+ << "new IceInternal.BasicStream(Ice.Util.getInstance(communicator));";
+ int iter = 0;
+ writeMarshalUnmarshalCode(out, "", indexTypes[i], keyS, true, iter, false);
+ assert(!indexTypes[i]->usesClasses());
+
+ out << nl << "java.nio.ByteBuffer buf = __os.prepareWrite();";
+ out << nl << "byte[] r = new byte[buf.limit()];";
+ out << nl << "buf.get(r);";
+ out << nl << "return r;";
+ }
+ out << eb;
+
+ //
+ // decodekey
+ //
+ out << sp << nl << "public Object";
+ out << nl << "decodeKey(byte[] bytes, Ice.Communicator communicator)";
+ out << sb;
+ if(dict.indices[i].member.empty())
+ {
+ //
+ // Decode the full value (with an encaps!)
+ //
+ out << nl << "return decodeValue(bytes, communicator);";
+ }
+ else
+ {
+ out << nl << "IceInternal.BasicStream __is = new IceInternal.BasicStream(Ice.Util.getInstance(communicator));";
+ out << nl << "__is.resize(bytes.length, true);";
+ out << nl << "java.nio.ByteBuffer buf = __is.prepareRead();";
+ out << nl << "buf.position(0);";
+ out << nl << "buf.put(bytes);";
+ out << nl << "buf.position(0);";
+
+ int iter = 0;
+ list<string> metaData;
+ string patchParams;
+
+ string typeS;
+ BuiltinPtr b = BuiltinPtr::dynamicCast(indexTypes[i]);
+ if(b != 0)
+ {
+ typeS = builtinTable[b->kind()];
+ }
+ else
+ {
+ typeS = typeToString(indexTypes[i], TypeModeIn);
+ }
+ out << nl << typeS << " r;";
+
+ if(b != 0)
+ {
+ switch(b->kind())
+ {
+ case Builtin::KindByte:
+ {
+ out << nl << "r = new java.lang.Byte(__is.readByte());";
+ break;
+ }
+ case Builtin::KindBool:
+ {
+ out << nl << "r = new java.lang.Boolean(__is.readBool());";
+ break;
+ }
+ case Builtin::KindShort:
+ {
+ out << nl << "r = new java.lang.Short(__is.readShort());";
+ break;
+ }
+ case Builtin::KindInt:
+ {
+ out << nl << "r = new java.lang.Integer(__is.readInt());";
+ break;
+ }
+ case Builtin::KindLong:
+ {
+ out << nl << "r = new java.lang.Long(__is.readLong());";
+ break;
+ }
+ case Builtin::KindFloat:
+ {
+ out << nl << "r = new java.lang.Float(__is.readFloat());";
+ break;
+ }
+ case Builtin::KindDouble:
+ {
+ out << nl << "r = new java.lang.Double(__is.readDouble());";
+ break;
+ }
+ case Builtin::KindString:
+ case Builtin::KindObject:
+ case Builtin::KindObjectProxy:
+ case Builtin::KindLocalObject:
+ {
+ writeMarshalUnmarshalCode(out, "", indexTypes[i], "r", false, iter, false, metaData, patchParams);
+ break;
+ }
+ }
+ }
+ else
+ {
+ writeMarshalUnmarshalCode(out, "", indexTypes[i], "r", false, iter, false, metaData, patchParams);
+ }
+ out << nl << "return r;";
+ }
+ out << eb;
+
+ //
+ // compare
+ //
+ out << sp << nl << "public int";
+ out << nl << "compare(Object o1, Object o2)";
+ out << sb;
+ out << nl << "assert _comparator != null;";
+ out << nl << "byte[] d1 = (byte[])o1;";
+ out << nl << "byte[] d2 = (byte[])o2;";
+ out << nl << "Ice.Communicator communicator = ((Freeze.Connection)_connection).getCommunicator();";
+ out << nl << "return _comparator.compare(";
+ out.inc();
+ out << nl << "decodeKey(d1, communicator),";
+ out << nl << "decodeKey(d2, communicator));";
+ out.dec();
+ out << eb;
+
+ //
+ // extractKey from value
+ //
+ out << sp << nl << "public Object";
+ out << nl << "extractKey(Object value)";
+ out << sb;
+ if(dict.indices[i].member.empty())
+ {
+ if(dict.indices[i].caseSensitive)
+ {
+ out << nl << "return value;";
+ }
+ else
+ {
+ out << nl << "return value.toLowerCase();";
+ }
}
else
{
out << nl << typeToString(valueType, TypeModeIn)
- << " __x = ("
- << typeToString(valueType, TypeModeIn) <<
- ")decodeValue(__value, ((Freeze.Connection)_connection).getCommunicator());";
- string param = "__x";
- if(!dict.indices[i].member.empty())
+ << " typedValue = ("
+ << typeToString(valueType, TypeModeIn) << ")value;";
+
+ string member = string("typedValue.") + dict.indices[i].member;
+ if(!dict.indices[i].caseSensitive)
{
- param += "." + dict.indices[i].member;
+ member += ".toLowerCase()";
}
- out << nl << "return encode" << indexClassName << "(" << param
- << ", ((Freeze.Connection)_connection).getCommunicator());";
+ out << nl << "return " << varToObject(indexTypes[i], member) << ";";
}
out << eb;
+
+ //
+ // marshalKey optimization
+ //
+ if(dict.indices[i].member.empty() && dict.indices[i].caseSensitive)
+ {
+ out << sp << nl << "protected byte[]";
+ out << nl << "marshalKey(byte[] value)";
+ out << sb;
+ out << nl << "return value;";
+ out << eb;
+ }
out << sp << nl << "private " << indexClassName << "(String name)";
out << sb;
diff --git a/cpp/test/Freeze/dbmap/Client.cpp b/cpp/test/Freeze/dbmap/Client.cpp
index 1dad4cca175..a47af217e30 100644
--- a/cpp/test/Freeze/dbmap/Client.cpp
+++ b/cpp/test/Freeze/dbmap/Client.cpp
@@ -623,7 +623,7 @@ run(const CommunicatorPtr& communicator, const string& envName)
cout << "testing sorting... " << flush;
{
- SortedMap sm(connection, "sortedIntIdentity");
+ SortedMap sm(connection, "sortedMap");
TransactionHolder txHolder(connection);
for(int i = 0; i < 1000; i++)
@@ -640,7 +640,7 @@ run(const CommunicatorPtr& communicator, const string& envName)
}
{
- SortedMap sm(connection, "sortedIntIdentity");
+ SortedMap sm(connection, "sortedMap");
{
for(int i = 0; i < 100; ++i)
{
diff --git a/java/CHANGES b/java/CHANGES
index 1caf3b7fc36..984582f3eca 100644
--- a/java/CHANGES
+++ b/java/CHANGES
@@ -1,6 +1,30 @@
Changes since version 2.1.2
---------------------------
+- Added custom-compare to Freeze Maps:
+ you can now provide your own Comparator object for keys and indices.
+ The constructor for a generated Freeze Map takes two new optional
+ parameters: a Comparator object (the comparator for the primary key)
+ and a java.util.Map string-to-Comparator, which provides the
+ Comparators for the indices. When the comparator is null or not
+ provided, Freeze compares binary strings encoded using the Ice
+ encoding.
+ Freeze Map now implements the java.util.SortedMap interface, and in
+ addition provides 4 new non-standard methods:
+ - SortedMap headMapForIndex(String indexName, Object toKey)
+ - SortedMap mapForIndex(String indexName)
+ - SortedMap subMapForIndex(String indexName, Object fromKey, Object toKey)
+ - SortedMap tailMapForIndex(String indexName, Object fromKey)
+
+ The returned SortedMap is a map index-key to java.util.Set of
+ Map.Entry objects of the main Freeze Map.
+
+ ((java.util.Set)map.subMapForIndex("foo").get(bar)).entrySet().iterator()
+ is similar but not identical to map.findByFoo(bar):
+ - the parameter ("bar") must be an Object (not a byte or int for example)
+ - get(bar) returns null if there is no entry with this index, while
+ findByFoo() always returns an iterator (sometimes with 0 elements).
+
- Fixed a deadlock during shutdown that could happen with
bi-directional connections.
diff --git a/java/src/Freeze/KeyCodec.java b/java/src/Freeze/KeyCodec.java
new file mode 100755
index 00000000000..3bed3d05e24
--- /dev/null
+++ b/java/src/Freeze/KeyCodec.java
@@ -0,0 +1,16 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2005 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package Freeze;
+
+public interface KeyCodec
+{
+ public abstract byte[] encodeKey(Object o, Ice.Communicator communicator);
+ public abstract Object decodeKey(byte[] b, Ice.Communicator communicator);
+}
diff --git a/java/src/Freeze/Map.java b/java/src/Freeze/Map.java
index b1b13ee2772..cca42f2aa16 100644
--- a/java/src/Freeze/Map.java
+++ b/java/src/Freeze/Map.java
@@ -9,31 +9,51 @@
package Freeze;
-public abstract class Map extends java.util.AbstractMap
+public abstract class Map extends java.util.AbstractMap
+ implements java.util.SortedMap, KeyCodec
{
+ public abstract byte[] encodeValue(Object o, Ice.Communicator communicator);
+ public abstract Object decodeValue(byte[] b, Ice.Communicator communicator);
+
public
- Map(Connection connection, String dbName, String key, String value, boolean createDb)
+ Map(Connection connection, String dbName, String key, String value,
+ boolean createDb, java.util.Comparator comparator)
{
_connection = (ConnectionI) connection;
+ _comparator = (comparator == null) ? null : new Comparator(comparator);
+
_errorPrefix = "Freeze DB DbEnv(\"" + _connection.envName() + "\") Db(\"" + dbName + "\"): ";
_trace = _connection.trace();
- init(null, dbName, key, value, createDb);
+ init(null, dbName, key, value, createDb, null);
}
protected
- Map(Connection connection, String dbName)
+ Map(Connection connection, String dbName, java.util.Comparator comparator)
{
_connection = (ConnectionI) connection;
+ _comparator = (comparator == null) ? null : new Comparator(comparator);
+
_errorPrefix = "Freeze DB DbEnv(\"" + _connection.envName() + "\") Db(\"" + dbName + "\"): ";
_trace = _connection.trace();
}
protected void
- init(Freeze.Map.Index[] indices, String dbName, String key, String value, boolean createDb)
+ init(Freeze.Map.Index[] indices, String dbName,
+ String key, String value, boolean createDb, java.util.Map indexComparators)
{
- _db = Freeze.SharedDb.get(_connection, dbName, key, value, indices, createDb);
+ _db = Freeze.SharedDb.get(_connection, dbName, key,
+ value, indices, createDb, _comparator,
+ indexComparators);
_token = _connection.registerMap(this);
+
+ if(indices != null)
+ {
+ for(int i = 0; i < indices.length; ++i)
+ {
+ _indexMap.put(indices[i].name(), indices[i]);
+ }
+ }
}
public void
@@ -41,17 +61,196 @@ public abstract class Map extends java.util.AbstractMap
{
close(false);
}
-
+
//
- // A concrete implementation of a Freeze.Map must provide
- // implementations of the following to encode & decode the
- // key/value pairs.
+ // SortedMap methods
//
- public abstract byte[] encodeKey(Object o, Ice.Communicator communicator);
- public abstract Object decodeKey(byte[] b, Ice.Communicator communicator);
- public abstract byte[] encodeValue(Object o, Ice.Communicator communicator);
- public abstract Object decodeValue(byte[] b, Ice.Communicator communicator);
+ public java.util.Comparator
+ comparator()
+ {
+ if(_comparator == null)
+ {
+ return null;
+ }
+ else
+ {
+ //
+ // Return's the user's comparator, not the DB comparator.
+ //
+ return _comparator.comparator();
+ }
+ }
+
+ public Object firstKey()
+ {
+ return firstKey(null, null);
+ }
+
+ public Object lastKey()
+ {
+ return lastKey(null, null);
+ }
+
+ Object firstKey(Object fromKey, Object toKey)
+ {
+ byte[] fk = fromKey == null ? null :
+ encodeKey(fromKey, _connection.communicator());
+
+ byte[] k = getFirstOrLastKey(_db.db(), _db.dbName(), fk, true);
+ if(k == null)
+ {
+ throw new NoSuchElementException();
+ }
+ else
+ {
+ Object key = decodeKey(k, _connection.communicator());
+ if(toKey != null && comparator().compare(key, toKey) >= 0)
+ {
+ throw new NoSuchElementException();
+ }
+ return key;
+ }
+ }
+
+ Object lastKey(Object fromKey, Object toKey)
+ {
+ byte[] tk = toKey == null ? null :
+ encodeKey(toKey, _connection.communicator());
+
+ byte[] k = getFirstOrLastKey(_db.db(), _db.dbName(), tk, false);
+ if(k == null)
+ {
+ throw new NoSuchElementException();
+ }
+ else
+ {
+ Object key = decodeKey(k, _connection.communicator());
+ if(fromKey != null && comparator().compare(fromKey, key) > 0)
+ {
+ throw new NoSuchElementException();
+ }
+ return key;
+ }
+ }
+
+ public java.util.SortedMap headMap(Object toKey)
+ {
+ if(toKey == null)
+ {
+ throw new NullPointerException();
+ }
+ if(_comparator == null)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ return new SubMap(this, null, toKey);
+ }
+
+ public java.util.SortedMap tailMap(Object fromKey)
+ {
+ if(fromKey == null)
+ {
+ throw new NullPointerException();
+ }
+ if(_comparator == null)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ return new SubMap(this, fromKey, null);
+ }
+
+ public java.util.SortedMap subMap(Object fromKey, Object toKey)
+ {
+ if(fromKey == null || toKey == null )
+ {
+ throw new NullPointerException();
+ }
+ if(_comparator == null)
+ {
+ throw new UnsupportedOperationException();
+ }
+ return new SubMap(this, fromKey, toKey);
+ }
+
+
+ //
+ // Additional non-standard xxMapForIndex methods
+ //
+ public java.util.SortedMap headMapForIndex(String indexName, Object toKey)
+ {
+ if(toKey == null)
+ {
+ throw new NullPointerException();
+ }
+
+ Map.Index index = (Map.Index)_indexMap.get(indexName);
+ if(index == null)
+ {
+ throw new IllegalArgumentException("Can't find index '" + indexName + "'");
+ }
+ else if(index.comparator() == null)
+ {
+ throw new IllegalArgumentException("Index '" + indexName + "' has no user-defined comparator");
+ }
+ return new SubMap(index, null, toKey);
+ }
+
+ public java.util.SortedMap tailMapForIndex(String indexName, Object fromKey)
+ {
+ if(fromKey == null)
+ {
+ throw new NullPointerException();
+ }
+ Map.Index index = (Map.Index)_indexMap.get(indexName);
+ if(index == null)
+ {
+ throw new IllegalArgumentException("Can't find index '" + indexName + "'");
+ }
+ else if(index.comparator() == null)
+ {
+ throw new IllegalArgumentException("Index '" + indexName + "' has no user-defined comparator");
+ }
+ return new SubMap(index, fromKey, null);
+ }
+
+ public java.util.SortedMap subMapForIndex(String indexName, Object fromKey, Object toKey)
+ {
+ if(fromKey == null || toKey == null )
+ {
+ throw new NullPointerException();
+ }
+ Map.Index index = (Map.Index)_indexMap.get(indexName);
+ if(index == null)
+ {
+ throw new IllegalArgumentException("Can't find index '" + indexName + "'");
+ }
+ else if(index.comparator() == null)
+ {
+ throw new IllegalArgumentException("Index '" + indexName + "' has no user-defined comparator");
+ }
+ return new SubMap(index, fromKey, toKey);
+ }
+
+ public java.util.SortedMap mapForIndex(String indexName)
+ {
+ Map.Index index = (Map.Index)_indexMap.get(indexName);
+ if(index == null)
+ {
+ throw new IllegalArgumentException("Can't find index '" + indexName + "'");
+ }
+ else if(index.comparator() == null)
+ {
+ throw new IllegalArgumentException("Index '" + indexName + "' has no user-defined comparator");
+ }
+ return new SubMap(index, null, null);
+ }
+
+ //
+ // Plain Map methods
+ //
public int
size()
{
@@ -111,7 +310,6 @@ public abstract class Map extends java.util.AbstractMap
Entry e = (Entry)p.next();
if(e.getValue() == null)
{
- p.close();
return true;
}
}
@@ -123,7 +321,6 @@ public abstract class Map extends java.util.AbstractMap
Entry e = (Entry)p.next();
if(value.equals(e.getValue()))
{
- p.close();
return true;
}
}
@@ -173,28 +370,21 @@ public abstract class Map extends java.util.AbstractMap
byte[] k = encodeKey(key, _connection.communicator());
com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
-
com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
dbValue.setPartial(true);
if(_trace >= 1)
{
- _connection.communicator().getLogger().trace("Freeze.Map", "checking key in Db \"" + _db.dbName() + "\"");
+ _connection.communicator().getLogger().trace(
+ "Freeze.Map", "checking key in Db \"" + _db.dbName() + "\"");
}
for(;;)
{
try
{
- com.sleepycat.db.OperationStatus rc = _db.db().get(_connection.dbTxn(), dbKey, dbValue, null);
- if(rc == com.sleepycat.db.OperationStatus.SUCCESS)
- {
- return true;
- }
- else
- {
- return false;
- }
+ return _db.db().get(_connection.dbTxn(), dbKey, dbValue, null)
+ == com.sleepycat.db.OperationStatus.SUCCESS;
}
catch(com.sleepycat.db.DeadlockException e)
{
@@ -209,11 +399,11 @@ public abstract class Map extends java.util.AbstractMap
{
if(_connection.deadlockWarning())
{
- _connection.communicator().getLogger().warning("Deadlock in Freeze.Map.containsKey while " +
- "reading Db \"" + _db.dbName() +
- "\"; retrying...");
+ _connection.communicator().getLogger().warning(
+ "Deadlock in Freeze.Map.containsKey while " +
+ "reading Db \"" + _db.dbName() +
+ "\"; retrying...");
}
-
//
// Try again
//
@@ -362,7 +552,7 @@ public abstract class Map extends java.util.AbstractMap
public java.util.Iterator
iterator()
{
- return new EntryIterator(null, null);
+ return new EntryIteratorImpl(null, null, null, false, false);
}
public boolean
@@ -432,7 +622,7 @@ public abstract class Map extends java.util.AbstractMap
Object obj = p.next();
if(obj != except)
{
- ((EntryIterator) obj).close(finalizing);
+ ((EntryIteratorImpl)obj).close(finalizing);
}
}
}
@@ -468,7 +658,25 @@ public abstract class Map extends java.util.AbstractMap
}
}
}
+
+ EntryIterator
+ createIterator(Index index, Object fromKey, Object toKey)
+ {
+ KeyCodec codec = index == null ? (KeyCodec)this : (KeyCodec)index;
+
+ Ice.Communicator communicator = _connection.getCommunicator();
+
+ return new EntryIteratorImpl(index,
+ fromKey == null ? null : codec.encodeKey(fromKey, communicator),
+ toKey == null ? null : codec.encodeKey(toKey, communicator),
+ false, true);
+ }
+ ConnectionI connection()
+ {
+ return _connection;
+ }
+
private static boolean
valEquals(Object o1, Object o2)
{
@@ -476,6 +684,128 @@ public abstract class Map extends java.util.AbstractMap
}
private byte[]
+ getFirstOrLastKey(com.sleepycat.db.Database db, String dbName, byte[] key, boolean first)
+ {
+ if(db == null)
+ {
+ DatabaseException ex = new DatabaseException();
+ ex.message = _errorPrefix + "\"" + dbName + "\" has been closed";
+ throw ex;
+ }
+
+ if(_trace >= 1)
+ {
+ _connection.communicator().getLogger().trace(
+ "Freeze.Map", "Searching db \"" + dbName + "\"");
+ }
+
+ com.sleepycat.db.DatabaseEntry dbKey = key == null ?
+ new com.sleepycat.db.DatabaseEntry():
+ new com.sleepycat.db.DatabaseEntry(key);
+
+ com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
+ dbValue.setPartial(true); // not interested in value
+
+ try
+ {
+ for(;;)
+ {
+ com.sleepycat.db.Cursor dbc = null;
+ try
+ {
+ dbc = db.openCursor(_connection.dbTxn(), null);
+ com.sleepycat.db.OperationStatus status;
+
+ if(key == null)
+ {
+ status = first ?
+ dbc.getFirst(dbKey, dbValue, null) : dbc.getLast(dbKey, dbValue, null);
+ }
+ else if(first)
+ {
+ status = dbc.getSearchKeyRange(dbKey, dbValue, null);
+ }
+ else
+ {
+ status = dbc.getSearchKeyRange(dbKey, dbValue, null);
+
+ if(status == com.sleepycat.db.OperationStatus.SUCCESS)
+ {
+ //
+ // goto the previous pair, which must be < key
+ //
+ status = dbc.getPrevNoDup(dbKey, dbValue, null);
+ }
+ else if(status == com.sleepycat.db.OperationStatus.NOTFOUND)
+ {
+ //
+ // All keys < desired toKey, so we pick the largest of
+ // all, the last one
+ //
+ status = dbc.getLast(dbKey, dbValue, null);
+ }
+ }
+
+ if(status == com.sleepycat.db.OperationStatus.SUCCESS)
+ {
+ return dbKey.getData();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ catch(com.sleepycat.db.DeadlockException dx)
+ {
+ if(_connection.dbTxn() != null)
+ {
+ DeadlockException ex = new DeadlockException();
+ ex.initCause(dx);
+ ex.message = _errorPrefix + "Dbc.getXXX: " + dx.getMessage();
+ throw ex;
+ }
+ else
+ {
+ if(_connection.deadlockWarning())
+ {
+ _connection.communicator().getLogger().warning(
+ "Deadlock in Freeze.Map while searching \"" + db.getDatabaseName() +
+ "\"; retrying...");
+ }
+
+ //
+ // Retry
+ //
+ }
+ }
+ finally
+ {
+ if(dbc != null)
+ {
+ try
+ {
+ dbc.close();
+ }
+ catch(com.sleepycat.db.DeadlockException dx)
+ {
+ //
+ // Ignored
+ //
+ }
+ }
+ }
+ }
+ }
+ catch(com.sleepycat.db.DatabaseException dx)
+ {
+ DatabaseException ex = new DatabaseException();
+ ex.initCause(dx);
+ ex.message = _errorPrefix + "Db.openCursor/Dbc.getXXX: " + dx.getMessage();
+ throw ex;
+ }
+ }
+
+ private byte[]
getImpl(com.sleepycat.db.DatabaseEntry dbKey)
{
if(_db == null)
@@ -666,7 +996,37 @@ public abstract class Map extends java.util.AbstractMap
}
}
- protected abstract class Index implements com.sleepycat.db.SecondaryKeyCreator
+ private class Comparator implements java.util.Comparator
+ {
+ Comparator(java.util.Comparator comparator)
+ {
+ _comparator = comparator;
+ }
+
+ public java.util.Comparator comparator()
+ {
+ return _comparator;
+ }
+
+ public int compare(Object o1, Object o2)
+ {
+ byte[] d1 = (byte[])o1;
+ byte[] d2 = (byte[])o2;
+
+ Ice.Communicator communicator = _connection.communicator();
+
+ return _comparator.compare(decodeKey(d1, communicator),
+ decodeKey(d2, communicator));
+ }
+
+ //
+ // The user-supplied comparator
+ //
+ private final java.util.Comparator _comparator;
+ }
+
+ public abstract class Index
+ implements com.sleepycat.db.SecondaryKeyCreator, java.util.Comparator, KeyCodec
{
//
// Implementation details
@@ -686,13 +1046,18 @@ public abstract class Map extends java.util.AbstractMap
result.setSize(secondaryKey.length);
return true;
}
-
+
com.sleepycat.db.SecondaryDatabase
db()
{
return _db;
}
+ String name()
+ {
+ return _name;
+ }
+
protected Index(String name)
{
_name = name;
@@ -700,10 +1065,13 @@ public abstract class Map extends java.util.AbstractMap
void
associate(String dbName, com.sleepycat.db.Database db,
- com.sleepycat.db.Transaction txn, boolean createDb)
+ com.sleepycat.db.Transaction txn, boolean createDb,
+ java.util.Comparator comparator)
throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException
{
_dbName = dbName + "." + _name;
+ _comparator = comparator;
+
assert(txn != null);
assert(_db == null);
@@ -712,6 +1080,10 @@ public abstract class Map extends java.util.AbstractMap
config.setAllowPopulate(true); // We always populate empty indices
config.setSortedDuplicates(true);
config.setType(com.sleepycat.db.DatabaseType.BTREE);
+ if(_comparator != null)
+ {
+ config.setBtreeComparator(this);
+ }
config.setKeyCreator(this);
_db = _connection.dbEnv().getEnv().openSecondaryDatabase(txn, _dbName, null, db, config);
@@ -724,6 +1096,59 @@ public abstract class Map extends java.util.AbstractMap
_dbName = from._dbName;
_db = from._db;
+ _comparator = from._comparator;
+ }
+
+ java.util.Comparator comparator()
+ {
+ return _comparator;
+ }
+
+ Map parent()
+ {
+ return Map.this;
+ }
+
+ Object firstKey(Object fromKey, Object toKey)
+ {
+ byte[] fk = fromKey == null ? null :
+ encodeKey(fromKey, _connection.communicator());
+
+ byte[] k = getFirstOrLastKey(_db, _dbName, fk, true);
+ if(k == null)
+ {
+ throw new NoSuchElementException();
+ }
+ else
+ {
+ Object key = decodeKey(k, _connection.communicator());
+ if(toKey != null && _comparator.compare(key, toKey) >= 0)
+ {
+ throw new NoSuchElementException();
+ }
+ return key;
+ }
+ }
+
+ Object lastKey(Object fromKey, Object toKey)
+ {
+ byte[] tk = toKey == null ? null :
+ encodeKey(toKey, _connection.communicator());
+
+ byte[] k = getFirstOrLastKey(_db, _dbName, tk, false);
+ if(k == null)
+ {
+ throw new NoSuchElementException();
+ }
+ else
+ {
+ Object key = decodeKey(k, _connection.communicator());
+ if(fromKey != null && _comparator.compare(fromKey, key) > 0)
+ {
+ throw new NoSuchElementException();
+ }
+ return key;
+ }
}
void close()
@@ -751,14 +1176,17 @@ public abstract class Map extends java.util.AbstractMap
}
public EntryIterator
- untypedFind(byte[] k)
+ untypedFind(Object key, boolean onlyDups)
{
- return new EntryIterator(this, k);
+ byte[] k = encodeKey(key, _connection.communicator());
+ return new EntryIteratorImpl(this, k, null, onlyDups, false);
}
public int
- untypedCount(byte[] k)
+ untypedCount(Object key)
{
+ byte[] k = encodeKey(key, _connection.communicator());
+
com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
//
@@ -823,27 +1251,102 @@ public abstract class Map extends java.util.AbstractMap
}
}
- protected abstract byte[]
- marshalKey(byte[] value);
+ boolean containsKey(Object key)
+ {
+ byte[] k = encodeKey(key, _connection.communicator());
+
+ com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
+ com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
+ dbValue.setPartial(true);
+
+ if(_trace >= 1)
+ {
+ _connection.communicator().getLogger().trace(
+ "Freeze.Map.Index", "checking key in Db \"" + _dbName + "\"");
+ }
+
+ for(;;)
+ {
+ try
+ {
+ return _db.get(_connection.dbTxn(), dbKey, dbValue, null)
+ == com.sleepycat.db.OperationStatus.SUCCESS;
+ }
+ catch(com.sleepycat.db.DeadlockException e)
+ {
+ if(_connection.dbTxn() != null)
+ {
+ DeadlockException ex = new DeadlockException();
+ ex.initCause(e);
+ ex.message = _errorPrefix + "Db.get: " + e.getMessage();
+ throw ex;
+ }
+ else
+ {
+ if(_connection.deadlockWarning())
+ {
+ _connection.communicator().getLogger().warning(
+ "Deadlock in Freeze.Map.Index.containsKey while " +
+ "reading Db \"" + _dbName + "\"; retrying...");
+ }
+ //
+ // Try again
+ //
+ }
+ }
+ catch(com.sleepycat.db.DatabaseException e)
+ {
+ DatabaseException ex = new DatabaseException();
+ ex.initCause(e);
+ ex.message = _errorPrefix + "Db.get: " + e.getMessage();
+ throw ex;
+ }
+ }
+ }
+
+ //
+ // Extracts the index key from this value
+ //
+ public abstract Object extractKey(Object value);
+
+ protected byte[] marshalKey(byte[] value)
+ {
+ Object decodedValue = decodeValue(value, _connection.communicator());
+ return encodeKey(extractKey(decodedValue), _connection.communicator());
+ }
+
+ //
+ // The user-supplied comparator
+ //
+ protected java.util.Comparator _comparator;
private String _name;
private String _dbName;
private com.sleepycat.db.SecondaryDatabase _db;
}
-
/**
*
- * The entry iterator class needs to be public to allow clients to
- * explicitly close the iterator and free resources allocated for
- * the iterator as soon as possible.
+ * The entry iterator allows clients to explicitly close the iterator
+ * and free resources allocated for the iterator as soon as possible.
*
**/
- public class EntryIterator implements java.util.Iterator
+ public interface EntryIterator extends java.util.Iterator
{
- EntryIterator(Index index, byte[] k)
+ void close();
+ void destroy(); // an alias for close
+ }
+
+ class EntryIteratorImpl implements EntryIterator
+ {
+ EntryIteratorImpl(Index index, byte[] fromKey, byte[] toKey,
+ boolean onlyFromKeyDups, boolean skipDups)
{
- _indexed = (index != null);
+ _index = index;
+ _fromKey = fromKey;
+ _toKey = toKey;
+ _onlyFromKeyDups = onlyFromKeyDups;
+ _skipDups = skipDups;
try
{
@@ -894,38 +1397,6 @@ public abstract class Map extends java.util.AbstractMap
throw ex;
}
- if(_indexed)
- {
- com.sleepycat.db.DatabaseEntry dbIKey = new com.sleepycat.db.DatabaseEntry(k);
- com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry();
- com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
-
- try
- {
- com.sleepycat.db.SecondaryCursor c = (com.sleepycat.db.SecondaryCursor)_cursor;
- if(c.getSearchKey(dbIKey, dbKey, dbValue, null) == com.sleepycat.db.OperationStatus.SUCCESS)
- {
- _current = new Entry(this, Map.this, _connection.communicator(), dbKey, dbValue.getData());
- }
- }
- catch(com.sleepycat.db.DeadlockException dx)
- {
- dead();
- DeadlockException ex = new DeadlockException();
- ex.initCause(dx);
- ex.message = _errorPrefix + "Dbc.get: " + dx.getMessage();
- throw ex;
- }
- catch(com.sleepycat.db.DatabaseException dx)
- {
- dead();
- DatabaseException ex = new DatabaseException();
- ex.initCause(dx);
- ex.message = _errorPrefix + "Dbc.get: " + dx.getMessage();
- throw ex;
- }
- }
-
synchronized(_iteratorList)
{
_iteratorList.addFirst(this);
@@ -938,47 +1409,60 @@ public abstract class Map extends java.util.AbstractMap
public boolean
hasNext()
{
- if(_indexed && _current == null)
- {
- return false;
- }
-
if(_current == null || _current == _lastReturned)
{
- //
- // Move _cursor, set _current
- //
-
com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry();
com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
+ com.sleepycat.db.DatabaseEntry dbIKey = new com.sleepycat.db.DatabaseEntry();
+ com.sleepycat.db.OperationStatus status = null;
try
{
- com.sleepycat.db.OperationStatus err;
- if(_indexed)
+ if(_index != null)
{
com.sleepycat.db.SecondaryCursor c = (com.sleepycat.db.SecondaryCursor)_cursor;
- com.sleepycat.db.DatabaseEntry dbIKey = new com.sleepycat.db.DatabaseEntry();
- //
- // dlen is 0, so we should not retrieve any value
- //
- dbIKey.setPartial(true);
-
- err = c.getNextDup(dbIKey, dbKey, dbValue, null);
- }
- else
- {
- err = _cursor.getNext(dbKey, dbValue, null);
- }
-
- if(err == com.sleepycat.db.OperationStatus.SUCCESS)
- {
- _current = new Entry(this, Map.this, _connection.communicator(), dbKey, dbValue.getData());
- return true;
+ if(_current == null)
+ {
+ //
+ // First key
+ //
+ if(_fromKey != null)
+ {
+ dbIKey.setData(_fromKey);
+ status = c.getSearchKey(dbIKey, dbKey, dbValue, null);
+ }
+ else
+ {
+ status = c.getFirst(dbIKey, dbKey, dbValue, null);
+ }
+ }
+ else
+ {
+ if(_onlyFromKeyDups)
+ {
+ status = c.getNextDup(dbIKey, dbKey, dbValue, null);
+ }
+ else if(_skipDups)
+ {
+ status = c.getNextNoDup(dbIKey, dbKey, dbValue, null);
+ }
+ else
+ {
+ status = c.getNext(dbIKey, dbKey, dbValue, null);
+ }
+ }
}
else
{
- return false;
+ if(_current == null && _fromKey != null)
+ {
+ dbKey.setData(_fromKey);
+ status = _cursor.getSearchKey(dbKey, dbValue, null);
+ }
+ else
+ {
+ status = _cursor.getNext(dbKey, dbValue, null);
+ }
}
}
catch(com.sleepycat.db.DeadlockException dx)
@@ -991,11 +1475,41 @@ public abstract class Map extends java.util.AbstractMap
}
catch(com.sleepycat.db.DatabaseException dx)
{
+ dead();
DatabaseException ex = new DatabaseException();
ex.initCause(dx);
ex.message = _errorPrefix + "Dbc.get: " + dx.getMessage();
throw ex;
}
+
+ if(status == com.sleepycat.db.OperationStatus.SUCCESS)
+ {
+ //
+ // Verify it's < _toKey
+ //
+ boolean inRange = true;
+ if(_toKey != null)
+ {
+ if(_index != null)
+ {
+ inRange = _index.compare(dbIKey,
+ new com.sleepycat.db.DatabaseEntry(_toKey)) < 0;
+ }
+ else
+ {
+ inRange = _comparator.compare(dbKey,
+ new com.sleepycat.db.DatabaseEntry(_toKey)) < 0;
+ }
+ }
+
+ if(inRange)
+ {
+ _current = new Entry(this, Map.this, _connection.communicator(), dbKey,
+ dbValue.getData(), dbIKey.getData());
+ return true;
+ }
+ }
+ return false;
}
else
{
@@ -1068,9 +1582,9 @@ public abstract class Map extends java.util.AbstractMap
//
// This works only for non-index iterators
//
- if(_indexed)
+ if(_index != null)
{
- throw new IllegalStateException();
+ throw new UnsupportedOperationException();
}
com.sleepycat.db.Cursor clone = null;
@@ -1234,11 +1748,10 @@ public abstract class Map extends java.util.AbstractMap
void
setValue(Map.Entry entry, Object value)
{
- if(_indexed)
+ if(_index != null)
{
- DatabaseException ex = new DatabaseException();
- ex.message = _errorPrefix + "Cannot set an iterator retrieved through an index";
- throw ex;
+ throw new UnsupportedOperationException(
+ _errorPrefix + "Cannot set an iterator retrieved through an index");
}
if(_txn != null)
@@ -1427,21 +1940,26 @@ public abstract class Map extends java.util.AbstractMap
private Entry _current;
private Entry _lastReturned;
private java.util.Iterator _iteratorListToken;
- private boolean _indexed;
-
+
+ private final Index _index;
+ private final byte[] _fromKey;
+ private final byte[] _toKey;
+ private final boolean _onlyFromKeyDups;
+ private final boolean _skipDups;
}
static class Entry implements java.util.Map.Entry
{
public
- Entry(Map.EntryIterator iterator, Map map, Ice.Communicator communicator,
- com.sleepycat.db.DatabaseEntry dbKey, byte[] valueBytes)
+ Entry(EntryIteratorImpl iterator, Map map, Ice.Communicator communicator,
+ com.sleepycat.db.DatabaseEntry dbKey, byte[] valueBytes, byte[] indexBytes)
{
_iterator = iterator;
_map = map;
_communicator = communicator;
_dbKey = dbKey;
_valueBytes = valueBytes;
+ _indexBytes = indexBytes;
}
public Object
@@ -1472,6 +1990,12 @@ public abstract class Map extends java.util.AbstractMap
return _value;
}
+ public byte[]
+ getIndexBytes()
+ {
+ return _indexBytes;
+ }
+
public Object
setValue(Object value)
{
@@ -1518,11 +2042,12 @@ public abstract class Map extends java.util.AbstractMap
return (o1 == null ? o2 == null : o1.equals(o2));
}
- private Map.EntryIterator _iterator;
+ private EntryIteratorImpl _iterator;
private Map _map;
private Ice.Communicator _communicator;
private com.sleepycat.db.DatabaseEntry _dbKey;
private byte[] _valueBytes;
+ private byte[] _indexBytes;
private Object _key;
private boolean _haveKey = false;
private Object _value;
@@ -1560,6 +2085,8 @@ public abstract class Map extends java.util.AbstractMap
}
protected ConnectionI _connection;
+ private final Comparator _comparator;
+
protected java.util.Iterator _token;
protected SharedDb _db;
protected String _errorPrefix;
@@ -1567,4 +2094,5 @@ public abstract class Map extends java.util.AbstractMap
private java.util.Set _entrySet;
private LinkedList _iteratorList = new LinkedList();
+ private java.util.Map _indexMap = new java.util.HashMap();
}
diff --git a/java/src/Freeze/SharedDb.java b/java/src/Freeze/SharedDb.java
index e192390db7e..35f6b4b9174 100755
--- a/java/src/Freeze/SharedDb.java
+++ b/java/src/Freeze/SharedDb.java
@@ -12,10 +12,12 @@ package Freeze;
class SharedDb
{
public static SharedDb
- get(ConnectionI connection, String dbName, String key, String value, Map.Index[] indices, boolean createDb)
+ get(ConnectionI connection, String dbName, String key, String value,
+ Map.Index[] indices, boolean createDb, java.util.Comparator comparator,
+ java.util.Map indexComparators)
{
MapKey mapKey = new MapKey(connection.envName(), connection.communicator(), dbName);
-
+
if(dbName.equals(Util.catalogName()))
{
//
@@ -34,7 +36,8 @@ class SharedDb
{
try
{
- result = new SharedDb(mapKey, key, value, connection, indices, createDb);
+ result = new SharedDb(mapKey, key, value, connection,
+ indices, createDb, comparator, indexComparators);
}
catch(com.sleepycat.db.DatabaseException dx)
{
@@ -147,7 +150,7 @@ class SharedDb
}
private SharedDb(MapKey mapKey, String key, String value, ConnectionI connection, Map.Index[] indices,
- boolean createDb)
+ boolean createDb, java.util.Comparator comparator, java.util.Map indexComparators)
throws com.sleepycat.db.DatabaseException
{
_mapKey = mapKey;
@@ -159,7 +162,7 @@ class SharedDb
try
{
Catalog catalog = new Catalog(catalogConnection, Util.catalogName(), true);
- CatalogData catalogData = (CatalogData) catalog.get(_mapKey.dbName);
+ CatalogData catalogData = (CatalogData)catalog.get(_mapKey.dbName);
if(catalogData != null)
{
if(catalogData.evictor)
@@ -186,6 +189,10 @@ class SharedDb
com.sleepycat.db.DatabaseConfig config = new com.sleepycat.db.DatabaseConfig();
config.setAllowCreate(createDb);
config.setType(com.sleepycat.db.DatabaseType.BTREE);
+ if(comparator != null)
+ {
+ config.setBtreeComparator(comparator);
+ }
if(_trace >= 1)
{
@@ -198,7 +205,13 @@ class SharedDb
{
for(int i = 0; i < _indices.length; ++i)
{
- _indices[i].associate(mapKey.dbName, _db, txn, createDb);
+ java.util.Comparator indexComparator = null;
+ if(indexComparators != null)
+ {
+ indexComparator = (java.util.Comparator)indexComparators.get(_indices[i].name());
+ }
+
+ _indices[i].associate(mapKey.dbName, _db, txn, createDb, indexComparator);
}
}
diff --git a/java/src/Freeze/SubMap.java b/java/src/Freeze/SubMap.java
new file mode 100755
index 00000000000..fdc9ca60fc9
--- /dev/null
+++ b/java/src/Freeze/SubMap.java
@@ -0,0 +1,476 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2005 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package Freeze;
+
+//
+// Sub-map of a Freeze Map or of another submap
+//
+//
+// When it's based of an index, the key is the index key and the value
+// is a Set of Map.Entry.
+//
+
+class SubMap extends java.util.AbstractMap implements java.util.SortedMap
+{
+ private class IndexValue extends java.util.AbstractSet
+ {
+ public java.util.Iterator
+ iterator()
+ {
+ return _index.untypedFind(_myKey, true);
+ }
+
+ public int
+ size()
+ {
+ return _index.untypedCount(_myKey);
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof IndexValue)
+ {
+ IndexValue indexValue = (IndexValue)o;
+ return indexValue._myKey.equals(_myKey);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public int hashCode()
+ {
+ return _myKey.hashCode();
+ }
+
+ private IndexValue(Object key)
+ {
+ _myKey = key;
+ }
+
+ private Object
+ getKey()
+ {
+ return _myKey;
+ }
+
+ private Object _myKey;
+ }
+
+ private class IndexEntry implements java.util.Map.Entry
+ {
+ public Object getKey()
+ {
+ return _value.getKey();
+ }
+
+ public Object getValue()
+ {
+ return _value;
+ }
+
+ public Object setValue(Object value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof IndexEntry)
+ {
+ IndexEntry indexEntry = (IndexEntry)o;
+ return indexEntry._value.equals(_value);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public int hashCode()
+ {
+ return _value.hashCode();
+ }
+
+ SubMap parent()
+ {
+ return SubMap.this;
+ }
+
+ private IndexEntry(Object key)
+ {
+ _value = new IndexValue(key);
+ }
+
+ private IndexValue _value;
+ }
+
+ private class IndexIterator implements Map.EntryIterator
+ {
+ public boolean hasNext()
+ {
+ return _iterator.hasNext();
+ }
+
+ public Object next()
+ {
+ Map.Entry entry = (Map.Entry)_iterator.next();
+ return new IndexEntry(_index.decodeKey(entry.getIndexBytes(),
+ _map.connection().communicator()));
+ }
+
+ public void remove()
+ {
+ _iterator.remove();
+ }
+
+ public void close()
+ {
+ _iterator.close();
+ }
+
+ public void destroy()
+ {
+ close();
+ }
+
+ private IndexIterator()
+ {
+ assert _index != null;
+ _iterator = _map.createIterator(_index, _fromKey, _toKey);
+ }
+
+ Map.EntryIterator _iterator;
+ }
+
+ SubMap(Map map, Object fromKey, Object toKey)
+ {
+ _fromKey = fromKey;
+ _toKey = toKey;
+ _map = map;
+ _index = null;
+
+ if(fromKey != null && toKey != null)
+ {
+ if(map.comparator().compare(fromKey, toKey) >= 0)
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ SubMap(Map.Index index, Object fromKey, Object toKey)
+ {
+ _fromKey = fromKey;
+ _toKey = toKey;
+ _map = index.parent();
+ _index = index;
+
+ if(fromKey != null && toKey != null)
+ {
+ if(index.comparator().compare(fromKey, toKey) >= 0)
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ private SubMap(SubMap subMap, Object fromKey, Object toKey)
+ {
+ _fromKey = fromKey;
+ _toKey = toKey;
+ _map = subMap._map;
+ _index = subMap._index;
+
+ if(fromKey != null && toKey != null)
+ {
+ if(comparator().compare(fromKey, toKey) >= 0)
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ //
+ // SortedMap methods
+ //
+ public java.util.Comparator comparator()
+ {
+ if(_index != null)
+ {
+ return _index.comparator();
+ }
+ else
+ {
+ return _map.comparator();
+ }
+ }
+
+ public Object firstKey()
+ {
+ return _index != null ?
+ _index.firstKey(_fromKey, _toKey) :
+ _map.firstKey(_fromKey, _toKey);
+ }
+
+ public Object lastKey()
+ {
+ return _index != null ?
+ _index.lastKey(_fromKey, _toKey) :
+ _map.lastKey(_fromKey, _toKey);
+ }
+
+ public java.util.SortedMap headMap(Object toKey)
+ {
+ if(toKey == null)
+ {
+ throw new NullPointerException();
+ }
+ return new SubMap(this, _fromKey, toKey);
+
+ }
+
+ public java.util.SortedMap tailMap(Object fromKey)
+ {
+ if(fromKey == null)
+ {
+ throw new NullPointerException();
+ }
+ return new SubMap(this, fromKey, _toKey);
+ }
+
+ public java.util.SortedMap subMap(Object fromKey, Object toKey)
+ {
+ if(fromKey == null || toKey == null )
+ {
+ throw new NullPointerException();
+ }
+ return new SubMap(this, fromKey, toKey);
+ }
+
+ //
+ // java.util.Map methods
+ //
+ public java.util.Set
+ entrySet()
+ {
+ if(_entrySet == null)
+ {
+ _entrySet = new java.util.AbstractSet()
+ {
+ public java.util.Iterator
+ iterator()
+ {
+ if(_index == null)
+ {
+ return _map.createIterator(_index, _fromKey, _toKey);
+ }
+ else
+ {
+ return new IndexIterator();
+ }
+ }
+
+ public boolean
+ contains(Object o)
+ {
+ if(_index == null)
+ {
+ //
+ // If the main map contains this object, check it's within [fromKey, toKey[
+ //
+ if(_map.entrySet().contains(o))
+ {
+ Map.Entry entry = (Map.Entry)o;
+ return inRange(entry.getKey());
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if(o instanceof IndexEntry)
+ {
+ IndexEntry indexEntry = (IndexEntry)o;
+ return indexEntry.parent() == SubMap.this &&
+ _index.containsKey(indexEntry.getKey());
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ public boolean
+ remove(Object o)
+ {
+ if(_index == null)
+ {
+ if(o instanceof Map.Entry)
+ {
+ Map.Entry entry = (Map.Entry)o;
+ return inRange(entry.getKey()) && _map.entrySet().remove(o);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ //
+ // Not yet implemented, should remove all objects that
+ // match this index-key
+ //
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public int
+ size()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean
+ isEmpty()
+ {
+ try
+ {
+ firstKey();
+ return false;
+ }
+ catch(NoSuchElementException e)
+ {
+ return true;
+ }
+ }
+ };
+ }
+ return _entrySet;
+ }
+
+ //
+ // Put is not implemented (you have to put in the main map view)
+ //
+
+
+ public boolean constainsKey(Object key)
+ {
+ if(!inRange(key))
+ {
+ return false;
+ }
+
+ //
+ // Then check if it's in the map
+ //
+ if(_index == null)
+ {
+ return _map.containsKey(key);
+ }
+ else
+ {
+ return _index.containsKey(key);
+ }
+ }
+
+
+ public Object
+ get(Object key)
+ {
+ if(!inRange(key))
+ {
+ return null;
+ }
+
+ if(_index == null)
+ {
+ return _map.get(key);
+ }
+ else
+ {
+ if(_index.containsKey(key))
+ {
+ return new IndexValue(key);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ public Object
+ remove(Object key)
+ {
+ if(!inRange(key))
+ {
+ return null;
+ }
+
+ if(_index == null)
+ {
+ return _map.remove(key);
+ }
+ else
+ {
+ //
+ // Not yet implemented
+ //
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public boolean
+ fastRemove(Object key)
+ {
+ if(!inRange(key))
+ {
+ return false;
+ }
+
+ if(_index == null)
+ {
+ return _map.fastRemove(key);
+ }
+ else
+ {
+ //
+ // Not yet implemented
+ //
+ throw new UnsupportedOperationException();
+ }
+ }
+
+
+ private boolean inRange(Object key)
+ {
+ if(_fromKey != null && comparator().compare(_fromKey, key) > 0)
+ {
+ return false;
+ }
+ if(_toKey != null && comparator().compare(key, _toKey) >= 0)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private final Object _fromKey;
+ private final Object _toKey;
+ private final Map _map;
+ private final Map.Index _index;
+ private java.util.Set _entrySet;
+}
diff --git a/java/test/Freeze/dbmap/Client.java b/java/test/Freeze/dbmap/Client.java
index 26005a66165..5d481ccb485 100644
--- a/java/test/Freeze/dbmap/Client.java
+++ b/java/test/Freeze/dbmap/Client.java
@@ -492,7 +492,7 @@ public class Client
e.setValue(new Integer(18));
test(false);
}
- catch(Freeze.DatabaseException ex)
+ catch(UnsupportedOperationException ex)
{
// Expected
}
@@ -621,9 +621,162 @@ public class Client
iim.closeAllIterators();
iim.clear();
}
-
System.out.println("ok");
+
+ System.out.print("testing sorting... ");
+ System.out.flush();
+
+
+ final java.util.Comparator less =
+ new java.util.Comparator()
+ {
+ public int compare(Object o1, Object o2)
+ {
+ if(o1 == o2)
+ {
+ return 0;
+ }
+ else if(o1 == null)
+ {
+ return -((Comparable)o2).compareTo(o1);
+ }
+ else
+ {
+ return ((Comparable)o1).compareTo(o2);
+ }
+ }
+ };
+
+ java.util.Comparator greater =
+ new java.util.Comparator()
+ {
+ public int compare(Object o1, Object o2)
+ {
+ return -less.compare(o1, o2);
+ }
+ };
+
+ java.util.Map indexComparators = new java.util.HashMap();
+ indexComparators.put("category", greater);
+ java.util.Random rand = new java.util.Random();
+
+ {
+ SortedMap sm = new SortedMap(connection, "sortedMap", true, less, indexComparators);
+
+ Transaction tx = connection.beginTransaction();
+ for(int i = 0; i < 500; i++)
+ {
+ int k = rand.nextInt(1000);
+
+ Ice.Identity id = new Ice.Identity("foo",
+ String.valueOf(alphabet.charAt(k % 26)));
+
+
+ sm.fastPut(new Integer(k), id);
+ }
+ tx.commit();
+ sm.close();
+ }
+
+ {
+ SortedMap sm = new SortedMap(connection, "sortedMap", true, less, indexComparators);
+
+ //
+ // Primary key
+ //
+ for(int i = 0; i < 100; i++)
+ {
+ int k = rand.nextInt(1000);
+
+ java.util.SortedMap subMap = sm.tailMap(new Integer(k));
+ try
+ {
+ Integer fk = (Integer)subMap.firstKey();
+ test(fk.intValue() >= k);
+ }
+ catch(NoSuchElementException e)
+ {
+ // Expected from time to time
+ }
+
+ subMap = sm.headMap(new Integer(k));
+ try
+ {
+ Integer lk = (Integer)subMap.lastKey();
+ test(lk.intValue() < k);
+ }
+ catch(NoSuchElementException e)
+ {
+ // Expected from time to time
+ }
+ }
+
+ //
+ // Category index
+ //
+ for(int i = 0; i < 100; i++)
+ {
+ int k = rand.nextInt(1000);
+ String category = String.valueOf(alphabet.charAt(k % 26));
+
+ java.util.SortedMap subMap = sm.tailMapForIndex("category", category);
+ try
+ {
+ String fk = (String)subMap.firstKey();
+ test(greater.compare(fk, category) >= 0);
+ }
+ catch(NoSuchElementException e)
+ {
+ // Expected from time to time
+ }
+
+ subMap = sm.headMapForIndex("category", category);
+ try
+ {
+ String lk = (String)subMap.lastKey();
+ test(greater.compare(lk, category) < 0);
+ }
+ catch(NoSuchElementException e)
+ {
+ // Expected from time to time
+ }
+ }
+
+ java.util.SortedMap subMap = sm.mapForIndex("category");
+ java.util.Iterator p = subMap.entrySet().iterator();
+ String category = null;
+ while(p.hasNext())
+ {
+ java.util.Map.Entry entry = (java.util.Map.Entry)p.next();
+
+ if(category != null)
+ {
+ test(greater.compare(category, entry.getKey()) < 0);
+ }
+ category = (String)entry.getKey();
+ // System.out.println("*******Category == " + category);
+
+
+ java.util.Iterator q = ((java.util.Set)entry.getValue()).iterator();
+ while(q.hasNext())
+ {
+ //
+ // All my map entries
+ //
+ entry = (java.util.Map.Entry)q.next();
+ Ice.Identity id = (Ice.Identity)entry.getValue();
+ test(category.equals(id.category));
+
+ // System.out.println("Key == " + entry.getKey().toString());
+
+ }
+ }
+ sm.closeAllIterators();
+ sm.clear();
+ sm.close();
+ }
+ System.out.println("ok");
connection.close();
return 0;
diff --git a/java/test/Freeze/dbmap/build.xml b/java/test/Freeze/dbmap/build.xml
index 98ab4aef2e3..dfb2897ecc4 100644
--- a/java/test/Freeze/dbmap/build.xml
+++ b/java/test/Freeze/dbmap/build.xml
@@ -51,6 +51,14 @@
<dict name="IntIdentityMapWithIndex" key="int" value="Ice::Identity"/>
<dictindex name="IntIdentityMapWithIndex" member="category"/>
</slice2freezej>
+ <slice2freezej ice="on" outputdir="${generated.dir}">
+ <includepath>
+ <pathelement path="${slice.dir}" />
+ </includepath>
+ <fileset dir="${slice.dir}/Ice" includes="Identity.ice"/>
+ <dict name="SortedMap" key="int" value="Ice::Identity"/>
+ <dictindex name="SortedMap" member="category"/>
+ </slice2freezej>
</target>
<target name="compile" depends="generate">