For a custom typehandler example we will try to write a very simple typehandler for StringBuffer(java) and StringBuilder(.NET). These classes are basically just another representation of String, so we can look at the StringHandler implementation in db4o source.
To keep it simple we will skip information required for indexing - please look at IndexableTypeHandler in db4o sources to get more information on how to handle indexes.
The first thing should be #write method, which determines how the object is persisted:
1public void Write(IWriteContext context, object obj) 2
{ 3
string str = ((StringBuilder)obj).ToString(); 4
IWriteBuffer buffer = context; 5
buffer.WriteInt(str.Length); 6
WriteToBuffer(buffer, str); 7
}
01private static void WriteToBuffer(IWriteBuffer buffer, string str) 02
{ 03
int length = str.Length; 04
char[] chars = new char[length]; 05
str.CopyTo(0, chars, 0, length); 06
for (int i = 0; i < length; i++) 07
{ 08
buffer.WriteByte((byte)(chars[i] & 0xff)); 09
buffer.WriteByte((byte)(chars[i] >> 8)); 10
} 11
}
1Public Sub Write(ByVal context As IWriteContext, ByVal obj As Object) Implements ITypeHandler4.Write 2
Dim str As String = DirectCast(obj, StringBuilder).ToString() 3
Dim buffer As IWriteBuffer = context 4
buffer.WriteInt(str.Length) 5
WriteToBuffer(buffer, str) 6
End Sub
1Private Shared Sub WriteToBuffer(ByVal buffer As IWriteBuffer, ByVal str As String) 2
Dim length As Integer = str.Length 3
Dim chars As Char() = New Char(length - 1) {} 4
str.CopyTo(0, chars, 0, length) 5
For i As Integer = 0 To length - 1 6
buffer.WriteByte(CByte(AscW(chars(i)) And 255)) 7
buffer.WriteByte(CByte(AscW(chars(i)) >> 8)) 8
Next 9
End Sub
As you can see from the code above, there are 3 steps:
Next step is to read the same from the buffer. It is just opposite to the write method:
01public object Read(IReadContext context) 02
{ 03
IReadBuffer buffer = context; 04
string str = ""; 05
int length = buffer.ReadInt(); 06
if (length > 0) 07
{ 08
str = ReadBuffer(buffer, length); 09
} 10
return new StringBuilder(str); 11
}
1private static string ReadBuffer(IReadBuffer buffer, int length) 2
{ 3
char[] chars = new char[length]; 4
for (int ii = 0; ii < length; ii++) 5
{ 6
chars[ii] = (char)((buffer.ReadByte() & 0xff) | ((buffer.ReadByte() & 0xff) << 8)); 7
} 8
return new string(chars, 0, length); 9
}
1Public Function Read(ByVal context As IReadContext) As Object Implements ITypeHandler4.Read 2
Dim buffer As IReadBuffer = context 3
Dim str As String = "" 4
Dim length As Integer = buffer.ReadInt() 5
If length > 0 Then 6
str = ReadBuffer(buffer, length) 7
End If 8
Return New StringBuilder(str) 9
End Function
1Private Shared Function ReadBuffer(ByVal buffer As IReadBuffer, ByVal length As Integer) As String 2
Dim chars As Char() = New Char(length - 1) {} 3
For ii As Integer = 0 To length - 1 4
chars(ii) = ChrW(((buffer.ReadByte() And 255) Or ((buffer.ReadByte() And 255) << 8))) 5
Next 6
Return New String(chars, 0, length) 7
End Function
Delete is simple - we just reposition the buffer offset to the end of the slot:
1public void Delete(IDeleteContext context) 2
{ 3
context.ReadSlot(); 4
}
1Public Sub Delete(ByVal context As IDeleteContext) Implements ITypeHandler4.Delete 2
context.ReadSlot() 3
End Sub
Try to experiment with the #delete method by implementing cascade on delete. Use FirstClassObjectHandler as an example.
We are done with the read/write operations. But as you remember, in order to read an object, we must find it through a query, and that's where we will need a #compare method (well, you do not need it if your query does not contain any comparison criteria, but this is normally not the case):
1public IPreparedComparison PrepareComparison(IContext con, object obj) 2
{ 3
return new PreparedComparison(obj); 4
}
01private class PreparedComparison : IPreparedComparison 02
{ 03
object _source = null; 04
05
public PreparedComparison(object source) 06
{ 07
_source = source; 08
} 09
10
public int CompareTo(object target) 11
{ 12
return Compare((StringBuilder)_source, (StringBuilder)target); 13
} 14
}
01private static int Compare(StringBuilder a_compare, StringBuilder a_with) 02
{ 03
if (a_compare == null) 04
{ 05
if (a_with == null) 06
{ 07
return 0; 08
} 09
return -1; 10
} 11
if (a_with == null) 12
{ 13
return 1; 14
} 15
char[] c_compare = new char[a_compare.Length]; 16
a_compare.CopyTo(0, c_compare, 0, a_compare.Length); 17
char[] c_with = new char[a_with.Length]; 18
a_with.CopyTo(0, c_with, 0, a_with.Length); 19
20
return CompareChars(c_compare, c_with); 21
}
01private static int CompareChars(char[] compare, char[] with) 02
{ 03
int min = compare.Length < with.Length ? compare.Length : with.Length; 04
for (int i = 0; i < min; i++) 05
{ 06
if (compare[i] != with[i]) 07
{ 08
return compare[i] - with[i]; 09
} 10
} 11
return compare.Length - with.Length; 12
}
1Public Function PrepareComparison(ByVal con As IContext, ByVal obj As Object) As IPreparedComparison Implements ITypeHandler4.PrepareComparison 2
Return New PreparedComparison(obj) 3
End Function
01Private Class PreparedComparison 02
Implements IPreparedComparison 03
Private _source As Object = Nothing 04
05
Public Sub New(ByVal source As Object) 06
_source = source 07
End Sub 08
09
Public Function CompareTo(ByVal target As Object) As Integer Implements IPreparedComparison.CompareTo 10
Return Compare(DirectCast(_source, StringBuilder), DirectCast(target, StringBuilder)) 11
End Function 12
End Class
01Private Shared Function Compare(ByVal a_compare As StringBuilder, ByVal a_with As StringBuilder) As Integer 02
If a_compare Is Nothing Then 03
If a_with Is Nothing Then 04
Return 0 05
End If 06
Return -1 07
End If 08
If a_with Is Nothing Then 09
Return 1 10
End If 11
Dim c_compare As Char() = New Char(a_compare.Length - 1) {} 12
a_compare.CopyTo(0, c_compare, 0, a_compare.Length) 13
Dim c_with As Char() = New Char(a_with.Length - 1) {} 14
a_with.CopyTo(0, c_with, 0, a_with.Length) 15
16
Return CompareChars(c_compare, c_with) 17
End Function
1Private Shared Function CompareChars(ByVal compare As Char(), ByVal [with] As Char()) As Integer 2
Dim min As Integer = IIf(compare.Length < [with].Length, compare.Length, [with].Length) 3
For i As Integer = 0 To min - 1 4
If compare(i) <> [with](i) Then 5
Return compare(i).CompareTo([with](i)) 6
End If 7
Next 8
Return compare.Length - [with].Length 9
End Function
The last method left: #defragment. This one only moves the offset to the beginning of the object data, i.e. skips Id and size information (to be compatible to older versions):
1public void Defragment(IDefragmentContext context) 2
{ 3
// To stay compatible with the old marshaller family 4
// In the marshaller family 0 number 8 represented 5
// length reqiored to store ID and object length information 6
context.IncrementOffset(8); 7
}
1Public Sub Defragment(ByVal context As IDefragmentContext) Implements ITypeHandler4.Defragment 2
' To stay compatible with the old marshaller family 3
' In the marshaller family 0 number 4 represented 4
' length reqiored to store ID and object length information 5
context.IncrementOffset(4) 6
End Sub
This Typehandler implementation can be tested with a class below. Please, pay special attention to #configure method, which adds StringBufferHandler/StringBuilderHandler to the database configuration:
001/* Copyright (C) 2004 - 2008 db4objects Inc. http://www.db4o.com */ 002
using System.Text; 003
using System.IO; 004
005
using Db4objects.Db4o; 006
using Db4objects.Db4o.Config; 007
using Db4objects.Db4o.Defragment; 008
using Db4objects.Db4o.Ext; 009
using Db4objects.Db4o.Query; 010
using Db4objects.Db4o.Reflect; 011
using Db4objects.Db4o.Reflect.Net; 012
using Db4objects.Db4o.Reflect.Generic; 013
using Db4objects.Db4o.Typehandlers; 014
015
namespace Db4objects.Db4odoc.Typehandler 016
{ 017
018
public class TypehandlerExample 019
{ 020
021
private readonly static string Db4oFileName = "reference.db4o"; 022
private static IObjectContainer _container = null; 023
024
025
public static void Main(string[] args) 026
{ 027
TestReadWriteDelete(); 028
//TestDefrag(); 029
TestCompare(); 030
} 031
// end Main 032
033
private class TypeHandlerPredicate : ITypeHandlerPredicate 034
{ 035
public bool Match(IReflectClass classReflector, int version) 036
{ 037
IReflector reflector = classReflector.Reflector(); 038
IReflectClass claxx = reflector.ForClass(typeof(StringBuilder)); 039
bool res = claxx.Equals(classReflector); 040
return res; 041
042
} 043
} 044
// end TypeHandlerPredicate 045
046
private static IConfiguration Configure() 047
{ 048
IConfiguration configuration = Db4oFactory.NewConfiguration(); 049
// add a custom typehandler support 050
051
configuration.RegisterTypeHandler(new TypeHandlerPredicate(), 052
new StringBuilderHandler()); 053
return configuration; 054
} 055
// end Configure 056
057
058
private static void TestReadWriteDelete() 059
{ 060
StoreCar(); 061
// Does it still work after close? 062
RetrieveCar(); 063
// Does deletion work? 064
DeleteCar(); 065
RetrieveCar(); 066
} 067
// end TestReadWriteDelete 068
069
private static void RetrieveCar() 070
{ 071
IObjectContainer container = Database(Configure()); 072
if (container != null) 073
{ 074
try 075
{ 076
IObjectSet result = container.Query(typeof(Car)); 077
Car car = null; 078
if (result.HasNext()) 079
{ 080
car = (Car)result.Next(); 081
} 082
System.Console.WriteLine("Retrieved: " + car); 083
} 084
finally 085
{ 086
CloseDatabase(); 087
} 088
} 089
} 090
// end RetrieveCar 091
092
private static void DeleteCar() 093
{ 094
IObjectContainer container = Database(Configure()); 095
if (container != null) 096
{ 097
try 098
{ 099
IObjectSet result = container.Query(typeof(Car)); 100
Car car = null; 101
if (result.HasNext()) 102
{ 103
car = (Car)result.Next(); 104
} 105
container.Delete(car); 106
System.Console.WriteLine("Deleted: " + car); 107
} 108
finally 109
{ 110
CloseDatabase(); 111
} 112
} 113
} 114
// end DeleteCar 115
116
private static void StoreCar() 117
{ 118
File.Delete(Db4oFileName); 119
IObjectContainer container = Database(Configure()); 120
if (container != null) 121
{ 122
try 123
{ 124
Car car = new Car("BMW"); 125
container.Store(car); 126
car = (Car)container.Query(typeof(Car)).Next(); 127
System.Console.WriteLine("Stored: " + car); 128
129
} 130
finally 131
{ 132
CloseDatabase(); 133
} 134
} 135
} 136
// end StoreCar 137
138
private static void TestCompare() 139
{ 140
File.Delete(Db4oFileName); 141
IObjectContainer container = Database(Configure()); 142
if (container != null) 143
{ 144
try 145
{ 146
Car car = new Car("BMW"); 147
container.Store(car); 148
car = new Car("Ferrari"); 149
container.Store(car); 150
car = new Car("Mercedes"); 151
container.Store(car); 152
IQuery query = container.Query(); 153
query.Constrain(typeof(Car)); 154
query.Descend("model").OrderAscending(); 155
IObjectSet result = query.Execute(); 156
ListResult(result); 157
158
} 159
finally 160
{ 161
CloseDatabase(); 162
} 163
} 164
} 165
// end TestCompare 166
167
private static void TestDefrag() 168
{ 169
File.Delete(Db4oFileName + ".backup"); 170
StoreCar(); 171
Defragment.Defrag(Db4oFileName); 172
RetrieveCar(); 173
} 174
// end TestDefrag 175
176
private static IObjectContainer Database(IConfiguration configuration) 177
{ 178
if (_container == null) 179
{ 180
try 181
{ 182
_container = Db4oFactory.OpenFile(configuration, Db4oFileName); 183
} 184
catch (DatabaseFileLockedException ex) 185
{ 186
System.Console.WriteLine(ex.Message); 187
} 188
} 189
return _container; 190
} 191
// end Database 192
193
private static void CloseDatabase() 194
{ 195
if (_container != null) 196
{ 197
_container.Close(); 198
_container = null; 199
} 200
} 201
// end CloseDatabase 202
203
204
private static void ListResult(IObjectSet result) 205
{ 206
System.Console.WriteLine(result.Size()); 207
while (result.HasNext()) 208
{ 209
System.Console.WriteLine(result.Next()); 210
} 211
} 212
// end ListResult 213
214
} 215
}
001' Copyright (C) 2004 - 2008 db4objects Inc. http://www.db4o.com 002
003
Imports System.Text 004
Imports System.IO 005
006
Imports Db4objects.Db4o 007
Imports Db4objects.Db4o.Config 008
Imports Db4objects.Db4o.Defragment 009
Imports Db4objects.Db4o.Ext 010
Imports Db4objects.Db4o.Query 011
Imports Db4objects.Db4o.Reflect 012
Imports Db4objects.Db4o.Reflect.Net 013
Imports Db4objects.Db4o.Reflect.Generic 014
Imports Db4objects.Db4o.Typehandlers 015
016
Namespace Db4objects.Db4odoc.Typehandler 017
018
Public Class TypehandlerExample 019
020
Private Shared ReadOnly Db4oFileName As String = "reference.db4o" 021
Private Shared _container As IObjectContainer = Nothing 022
023
024
Public Shared Sub Main(ByVal args As String()) 025
TestReadWriteDelete() 026
'TestDefrag() 027
TestCompare() 028
End Sub 029
' end Main 030
031
Private Class TypeHandlerPredicate 032
Implements ITypeHandlerPredicate 033
034
Public Function Match(ByVal classReflector As IReflectClass, ByVal version As Integer) As Boolean Implements ITypeHandlerPredicate.Match 035
Dim reflector As IReflector = classReflector.Reflector() 036
Dim claxx As IReflectClass = reflector.ForClass(GetType(StringBuilder)) 037
Dim res As Boolean = claxx Is classReflector 038
Return res 039
040
End Function 041
042
End Class 043
' end TypeHandlerPredicate 044
045
Private Shared Function Configure() As IConfiguration 046
Dim configuration As IConfiguration = Db4oFactory.NewConfiguration() 047
' add a custom typehandler support 048
049
configuration.RegisterTypeHandler(New TypeHandlerPredicate(), New StringBuilderHandler()) 050
Return configuration 051
End Function 052
' end Configure 053
054
055
Private Shared Sub TestReadWriteDelete() 056
StoreCar() 057
' Does it still work after close? 058
RetrieveCar() 059
' Does deletion work? 060
DeleteCar() 061
RetrieveCar() 062
End Sub 063
' end TestReadWriteDelete 064
065
Private Shared Sub RetrieveCar() 066
Dim container As IObjectContainer = Database(Configure()) 067
If container IsNot Nothing Then 068
Try 069
Dim query As IQuery = container.Query() 070
query.Constrain(GetType(Car)) 071
Dim result As IObjectSet = query.Execute() 072
Dim car As Car = Nothing 073
If result.HasNext() Then 074
car = DirectCast(result.[Next](), Car) 075
End If 076
If car Is Nothing Then 077
System.Console.WriteLine("Retrieved: Nothing") 078
Else 079
System.Console.WriteLine("Retrieved: " + car.ToString()) 080
End If 081
082
Finally 083
CloseDatabase() 084
End Try 085
End If 086
End Sub 087
' end RetrieveCar 088
089
Private Shared Sub DeleteCar() 090
Dim container As IObjectContainer = Database(Configure()) 091
If container IsNot Nothing Then 092
Try 093
Dim result As IObjectSet = container.Query(GetType(Car)) 094
Dim car As Car = Nothing 095
If result.HasNext() Then 096
car = DirectCast(result.[Next](), Car) 097
End If 098
container.Delete(car) 099
System.Console.WriteLine("Deleted: " + car.ToString()) 100
Finally 101
CloseDatabase() 102
End Try 103
End If 104
End Sub 105
' end DeleteCar 106
107
Private Shared Sub StoreCar() 108
File.Delete(Db4oFileName) 109
Dim container As IObjectContainer = Database(Configure()) 110
If container IsNot Nothing Then 111
Try 112
Dim car As New Car("BMW") 113
container.Store(car) 114
car = DirectCast(container.Query(GetType(Car)).[Next](), Car) 115
116
System.Console.WriteLine("Stored: " + car.ToString()) 117
Finally 118
CloseDatabase() 119
End Try 120
End If 121
End Sub 122
' end StoreCar 123
124
Private Shared Sub TestCompare() 125
File.Delete(Db4oFileName) 126
Dim container As IObjectContainer = Database(Configure()) 127
If container IsNot Nothing Then 128
Try 129
Dim car As New Car("BMW") 130
container.Store(car) 131
car = New Car("Ferrari") 132
container.Store(car) 133
car = New Car("Mercedes") 134
container.Store(car) 135
Dim query As IQuery = container.Query() 136
query.Constrain(GetType(Car)) 137
query.Descend("model").OrderAscending() 138
Dim result As IObjectSet = query.Execute() 139
140
ListResult(result) 141
Finally 142
CloseDatabase() 143
End Try 144
End If 145
End Sub 146
' end TestCompare 147
148
Private Shared Sub TestDefrag() 149
File.Delete(Db4oFileName + ".backup") 150
StoreCar() 151
Defragment.Defrag(Db4oFileName) 152
RetrieveCar() 153
End Sub 154
' end TestDefrag 155
156
Private Shared Function Database(ByVal configuration As IConfiguration) As IObjectContainer 157
If _container Is Nothing Then 158
Try 159
_container = Db4oFactory.OpenFile(configuration, Db4oFileName) 160
Catch ex As DatabaseFileLockedException 161
System.Console.WriteLine(ex.Message) 162
End Try 163
End If 164
Return _container 165
End Function 166
' end Database 167
168
Private Shared Sub CloseDatabase() 169
If _container IsNot Nothing Then 170
_container.Close() 171
_container = Nothing 172
End If 173
End Sub 174
' end CloseDatabase 175
176
177
Private Shared Sub ListResult(ByVal result As IObjectSet) 178
System.Console.WriteLine(result.Size()) 179
While result.HasNext() 180
System.Console.WriteLine(result.[Next]()) 181
End While 182
End Sub 183
' end ListResult 184
185
End Class 186
End Namespace