* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / FirebirdSql.Data.Firebird / FirebirdSql.Data.Embedded / FesArray.cs
1 /*
2  *      Firebird ADO.NET Data provider for .NET and Mono 
3  * 
4  *         The contents of this file are subject to the Initial 
5  *         Developer's Public License Version 1.0 (the "License"); 
6  *         you may not use this file except in compliance with the 
7  *         License. You may obtain a copy of the License at 
8  *         http://www.firebirdsql.org/index.php?op=doc&id=idpl
9  *
10  *         Software distributed under the License is distributed on 
11  *         an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 
12  *         express or implied. See the License for the specific 
13  *         language governing rights and limitations under the License.
14  * 
15  *      Copyright (c) 2002, 2005 Carlos Guzman Alvarez
16  *      All Rights Reserved.
17  */
18
19 using System;
20 using System.Collections;
21 using System.Globalization;
22 using System.IO;
23 using System.Net;
24 using System.Runtime.InteropServices;
25 using System.Text;
26
27 using FirebirdSql.Data.Common;
28
29 namespace FirebirdSql.Data.Embedded
30 {
31         internal sealed class FesArray : ArrayBase
32         {
33                 #region Fields
34
35                 private long                    handle;
36                 private FesDatabase             db;
37                 private FesTransaction  transaction;
38
39                 #endregion
40
41                 #region Properties
42
43                 public override long Handle
44                 {
45                         get     { return this.handle; }
46                         set     { this.handle = value; }
47                 }
48
49                 public override IDatabase DB
50                 {
51                         get     { return this.db; }
52                         set     { this.db = (FesDatabase)value; }
53                 }
54
55                 public override ITransaction Transaction
56                 {
57                         get     { return this.transaction; }
58                         set     { this.transaction = (FesTransaction)value;     }
59                 }
60
61                 #endregion
62
63                 #region Constructors
64
65                 public FesArray(ArrayDesc descriptor) : base(descriptor)
66                 {
67                 }
68
69                 public FesArray(
70                         IDatabase               db,
71                         ITransaction    transaction,
72                         string                  tableName, 
73                         string                  fieldName) : this(db, transaction, -1, tableName, fieldName)
74                 {
75                 }
76
77                 public FesArray(
78                         IDatabase               db,
79                         ITransaction    transaction,
80                         long                    handle, 
81                         string                  tableName, 
82                         string                  fieldName)      : base(tableName, fieldName)
83                 {
84                         if (!(db is     FesDatabase))
85                         {
86                                 throw new ArgumentException("Specified argument is not of GdsDatabase type.");
87                         }
88                         if (!(transaction is FesTransaction))
89                         {
90                                 throw new ArgumentException("Specified argument is not of GdsTransaction type.");
91                         }
92                         this.db                  = (FesDatabase)db;
93                         this.transaction = (FesTransaction)transaction;
94                         this.handle              = handle;
95
96                         this.LookupBounds();
97                 }
98
99                 #endregion
100
101                 #region Methods
102
103                 public override byte[] GetSlice(int     sliceLength)
104                 {
105                         int[] statusVector = FesConnection.GetNewStatusVector();
106
107                         int     dbHandle = this.db.Handle;
108                         int     trHandle = this.transaction.Handle;
109
110                         ArrayDescMarshaler marshaler = ArrayDescMarshaler.GetInstance();
111
112                         IntPtr arrayDesc = marshaler.MarshalManagedToNative(this.Descriptor);
113
114                         byte[] buffer = new     byte[sliceLength];
115
116                         FbClient.isc_array_get_slice(
117                                 statusVector,
118                                 ref     dbHandle,
119                                 ref     trHandle,
120                                 ref     this.handle,
121                                 arrayDesc,
122                                 buffer,
123                                 ref     sliceLength);
124                         
125                         // Free memory
126                         marshaler.CleanUpNativeData(ref arrayDesc);
127
128                         FesConnection.ParseStatusVector(statusVector);
129
130                         return buffer;
131                 }
132
133                 public override void PutSlice(System.Array sourceArray, int     sliceLength)
134                 {
135                         int[] statusVector = FesConnection.GetNewStatusVector();
136
137                         int     dbHandle = this.db.Handle;
138                         int     trHandle = this.transaction.Handle;
139
140                         ArrayDescMarshaler marshaler = ArrayDescMarshaler.GetInstance();
141
142                         IntPtr arrayDesc = marshaler.MarshalManagedToNative(this.Descriptor);
143
144                         // Obtain the System of type of Array elements and
145                         // Fill buffer
146                         Type systemType = this.GetSystemType();
147
148                         byte[] buffer = new     byte[sliceLength];
149                         if (systemType.IsPrimitive)
150                         {
151                                 Buffer.BlockCopy(sourceArray, 0, buffer, 0,     buffer.Length);
152                         }
153                         else
154                         {
155                                 buffer = this.EncodeSlice(this.Descriptor, sourceArray, sliceLength);
156                         }
157
158                         FbClient.isc_array_put_slice(
159                                 statusVector,
160                                 ref     dbHandle,
161                                 ref     trHandle,
162                                 ref     this.handle,
163                                 arrayDesc,
164                                 buffer,
165                                 ref     sliceLength);
166                         
167                         // Free memory
168                         marshaler.CleanUpNativeData(ref arrayDesc);
169
170                         FesConnection.ParseStatusVector(statusVector);
171                 }
172
173                 #endregion
174
175                 #region Protected Methods
176
177                 protected override System.Array DecodeSlice(byte[] slice)
178                 {
179                         Array           sliceData        = null;
180                         int                     slicePosition = 0;
181                         int                     type             = 0;
182                         DbDataType      dbType           = DbDataType.Array;
183                         Type            systemType       = this.GetSystemType();
184                         Charset         charset          = this.db.Charset;
185                         int[]           lengths          = new int[this.Descriptor.Dimensions];
186                         int[]           lowerBounds      = new int[this.Descriptor.Dimensions];                 
187
188                         // Get upper and lower bounds of each dimension
189                         for     (int i = 0;     i <     this.Descriptor.Dimensions;     i++)
190                         {
191                                 lowerBounds[i]  = this.Descriptor.Bounds[i].LowerBound;
192                                 lengths[i]              = this.Descriptor.Bounds[i].UpperBound;
193
194                                 if (lowerBounds[i] == 0)
195                                 {
196                                         lengths[i]++;
197                                 }
198                         }
199                         
200                         // Create slice arrays
201                         sliceData = Array.CreateInstance(systemType, lengths, lowerBounds);
202
203                         Array tempData = Array.CreateInstance(systemType, sliceData.Length);
204
205                         // Infer data types
206                         type = TypeHelper.GetFbType(this.Descriptor.DataType);
207                         dbType = TypeHelper.GetDbDataType(this.Descriptor.DataType, 0,  this.Descriptor.Scale);
208
209                         int     itemLength = this.Descriptor.Length;
210
211                         for     (int i = 0;     i <     tempData.Length; i++)
212                         {
213                                 if (slicePosition >= slice.Length)
214                                 {
215                                         break;
216                                 }
217                                                                 
218                                 switch(dbType)
219                                 {                                                       
220                                         case DbDataType.Char:
221                                                 tempData.SetValue(charset.GetString(slice, slicePosition, itemLength), i);
222                                                 break;
223
224                                         case DbDataType.VarChar:
225                                         {
226                                                 int     index = slicePosition;
227                                                 int     count = 0;
228                                                 while (slice[index++] != 0)
229                                                 {
230                                                         count ++;
231                                                 }
232                                                 tempData.SetValue(charset.GetString(slice, slicePosition, count), i);
233
234                                                 slicePosition += 2;
235                                         }
236                                         break;
237                                         
238                                         case DbDataType.SmallInt:
239                                                 tempData.SetValue(BitConverter.ToInt16(slice, slicePosition), i);
240                                                 break;
241         
242                                         case DbDataType.Integer:
243                                                 tempData.SetValue(BitConverter.ToInt32(slice, slicePosition), i);
244                                                 break;
245         
246                                         case DbDataType.BigInt:
247                                                 tempData.SetValue(BitConverter.ToInt64(slice, slicePosition), i);
248                                                 break;
249                                         
250                                         case DbDataType.Decimal:
251                                         case DbDataType.Numeric:
252                                         {
253                                                 object evalue = null;
254
255                                                 switch (type)
256                                                 {
257                                                         case IscCodes.SQL_SHORT:
258                                                                 evalue = BitConverter.ToInt16(slice, slicePosition);
259                                                                 break;
260
261                                                         case IscCodes.SQL_LONG:
262                                                                 evalue = BitConverter.ToInt32(slice, slicePosition);
263                                                                 break;
264
265                                                         case IscCodes.SQL_QUAD:
266                                                         case IscCodes.SQL_INT64:
267                                                                 evalue = BitConverter.ToInt64(slice, slicePosition);
268                                                                 break;
269                                                 }
270
271                                                 decimal dvalue = TypeDecoder.DecodeDecimal(evalue, this.Descriptor.Scale, type);
272
273                                                 tempData.SetValue(dvalue, i);
274                                         }
275                                         break;
276
277                                         case DbDataType.Double: 
278                                                 tempData.SetValue(BitConverter.ToDouble(slice, slicePosition), i);
279                                                 break;
280         
281                                         case DbDataType.Float:
282                                                 tempData.SetValue(BitConverter.ToSingle(slice, slicePosition), i);
283                                                 break;
284
285                                         case DbDataType.Date:
286                                         {
287                                                 int     idate = BitConverter.ToInt32(slice,     slicePosition);
288
289                                                 DateTime date = TypeDecoder.DecodeDate(idate);
290                                                 
291                                                 tempData.SetValue(date, i);
292                                         }
293                                         break;
294                                         
295                                         case DbDataType.Time:
296                                         {
297                                                 int     itime = BitConverter.ToInt32(slice,     slicePosition);
298                                         
299                                                 DateTime time = TypeDecoder.DecodeTime(itime);                                  
300                                                 
301                                                 tempData.SetValue(time, i);
302                                         }
303                                         break;
304                                                                                 
305                                         case DbDataType.TimeStamp:
306                                         {
307                                                 int     idate = BitConverter.ToInt32(slice,     slicePosition);
308                                                 int     itime = BitConverter.ToInt32(slice,     slicePosition + 4);
309                                         
310                                                 DateTime date = TypeDecoder.DecodeDate(idate);
311                                                 DateTime time = TypeDecoder.DecodeTime(itime);
312
313                                                 DateTime timestamp = new System.DateTime(
314                                                         date.Year, date.Month, date.Day,
315                                                         time.Hour,time.Minute, time.Second,     time.Millisecond);
316                                                                                         
317                                                 tempData.SetValue(timestamp, i);
318                                         }
319                                         break;
320                                 }
321                                 
322                                 slicePosition += itemLength;
323                         }
324                         
325                         if (systemType.IsPrimitive)
326                         {
327                                 // For primitive types we can use System.Buffer to copy generated data to destination array
328                                 Buffer.BlockCopy(tempData, 0, sliceData, 0,     Buffer.ByteLength(tempData));
329                         }
330                         else
331                         {
332                                 sliceData = tempData;   
333                         }
334                         
335                         return sliceData;
336                 }
337
338                 #endregion
339
340                 #region Private Metods
341
342                 private byte[] EncodeSlice(ArrayDesc desc, Array sourceArray, int length)
343                 {
344                         BinaryWriter    writer  = new BinaryWriter(new MemoryStream());
345                         IEnumerator             i               = sourceArray.GetEnumerator();
346                         Charset                 charset = this.db.Charset;
347                         DbDataType              dbType  = DbDataType.Array;
348                         int                             type    = 0;
349                         int                             subtype = (this.Descriptor.Scale < 0) ? 2 : 0;
350
351                         // Infer data types
352                         type = TypeHelper.GetFbType(this.Descriptor.DataType);
353                         dbType = TypeHelper.GetDbDataType(this.Descriptor.DataType, subtype,    this.Descriptor.Scale);
354
355                         while (i.MoveNext())
356                         {
357                                 switch (dbType)
358                                 {
359                                         case DbDataType.Char:
360                                         {
361                                                 string value  = i.Current != null ?     (string)i.Current :     String.Empty;
362                                                 byte[] buffer = charset.GetBytes(value);
363
364                                                 writer.Write(buffer);
365
366                                                 if (desc.Length > buffer.Length)
367                                                 {
368                                                         for     (int j = buffer.Length; j <     desc.Length; j++)
369                                                         {
370                                                                 writer.Write((byte)32);
371                                                         }
372                                                 }
373                                         }
374                                         break;
375
376                                         case DbDataType.VarChar:
377                                         {
378                                                 string value = i.Current !=     null ? (string)i.Current : String.Empty;
379
380                                                 value = value.TrimEnd();
381
382                                                 byte[] buffer = charset.GetBytes(value);
383                                                 writer.Write(buffer);
384
385                                                 if (desc.Length > buffer.Length)
386                                                 {
387                                                         for     (int j = buffer.Length; j <     desc.Length; j++)
388                                                         {
389                                                                 writer.Write((byte)0);
390                                                         }
391                                                 }
392                                                 writer.Write((short)0);
393                                         }
394                                         break;
395         
396                                         case DbDataType.SmallInt:
397                                                 writer.Write((short)i.Current);
398                                                 break;
399         
400                                         case DbDataType.Integer:
401                                                 writer.Write((int)i.Current);
402                                                 break;
403
404                                         case DbDataType.BigInt:
405                                                 writer.Write((long)i.Current);
406                                                 break;
407                                         
408                                         case DbDataType.Float:
409                                                 writer.Write((float)i.Current);
410                                                 break;
411                                                                                 
412                                         case DbDataType.Double:
413                                                 writer.Write((double)i.Current);
414                                                 break;
415                                         
416                                         case DbDataType.Numeric:
417                                         case DbDataType.Decimal:
418                                         {
419                                                 object numeric = TypeEncoder.EncodeDecimal((decimal)i.Current, desc.Scale, type);
420
421                                                 switch (type)
422                                                 {
423                                                         case IscCodes.SQL_SHORT:
424                                                                 writer.Write((short)numeric);
425                                                                 break;
426
427                                                         case IscCodes.SQL_LONG:
428                                                                 writer.Write((int)numeric);
429                                                                 break;
430
431                                                         case IscCodes.SQL_QUAD:
432                                                         case IscCodes.SQL_INT64:
433                                                                 writer.Write((long)numeric);
434                                                                 break;
435                                                 }
436                                         }
437                                         break;
438
439                                         case DbDataType.Date:
440                                                 writer.Write(TypeEncoder.EncodeDate(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat)));
441                                                 break;
442                                         
443                                         case DbDataType.Time:
444                                                 writer.Write(TypeEncoder.EncodeTime(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat)));
445                                                 break;
446
447                                         case DbDataType.TimeStamp:
448                                                 writer.Write(TypeEncoder.EncodeDate(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat)));
449                                                 writer.Write(TypeEncoder.EncodeTime(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat)));
450                                                 break;
451                                         
452                                         default:
453                                                 throw new NotSupportedException("Unknown data type");
454                                 }
455                         }
456
457                         return ((MemoryStream)writer.BaseStream).ToArray();
458                 }
459
460                 #endregion
461         }
462 }