diff options
author | Bernard Normier <bernard@zeroc.com> | 2005-11-11 19:37:06 +0000 |
---|---|---|
committer | Bernard Normier <bernard@zeroc.com> | 2005-11-11 19:37:06 +0000 |
commit | 0fd5ba1503be1db3377cc856ec79ed4902a062db (patch) | |
tree | 8305898e309a3b91392dbefad8f70ca0d38eaeb1 | |
parent | update PYTHONPATH paths (diff) | |
download | ice-0fd5ba1503be1db3377cc856ec79ed4902a062db.tar.bz2 ice-0fd5ba1503be1db3377cc856ec79ed4902a062db.tar.xz ice-0fd5ba1503be1db3377cc856ec79ed4902a062db.zip |
Freeze Map improvements (bug #415)
-rw-r--r-- | cpp/include/Freeze/Map.h | 1 | ||||
-rw-r--r-- | cpp/src/slice2freezej/Main.cpp | 511 | ||||
-rw-r--r-- | cpp/test/Freeze/dbmap/Client.cpp | 4 | ||||
-rw-r--r-- | java/CHANGES | 24 | ||||
-rwxr-xr-x | java/src/Freeze/KeyCodec.java | 16 | ||||
-rw-r--r-- | java/src/Freeze/Map.java | 768 | ||||
-rwxr-xr-x | java/src/Freeze/SharedDb.java | 25 | ||||
-rwxr-xr-x | java/src/Freeze/SubMap.java | 476 | ||||
-rw-r--r-- | java/test/Freeze/dbmap/Client.java | 157 | ||||
-rw-r--r-- | java/test/Freeze/dbmap/build.xml | 8 |
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"> |