1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 using System;
6 using System.Collections.Generic;
7 using System.Reflection;
8 
9 namespace IceInternal
10 {
11     public sealed class Patcher
12     {
13         static public System.Action<T> arrayReadValue<T>(T[] arr, int index) where T : Ice.Value
14         {
15             return (T v) => { arr[index] = v; };
16         }
17 
18         static public System.Action<T> listReadValue<T>(List<T> seq, int index) where T : Ice.Value
19         {
20             return (T v) => {
21                 int count = seq.Count;
22                 if(index >= count) // Need to grow the sequence.
23                 {
24                     for(int i = count; i < index; i++)
25                     {
26                         seq.Add(default(T));
27                     }
28                     seq.Add(v);
29                 }
30                 else
31                 {
32                     seq[index] = v;
33                 }
34             };
35         }
36 
37         static public System.Action<T> customSeqReadValue<T>(IEnumerable<T> seq, int index) where T : Ice.Value
38         {
39             return (T v) => {
40                 var info = getInvokeInfo<T>(seq.GetType());
41                 int count = info.getCount(seq);
42                 if(index >= count) // Need to grow the sequence.
43                 {
44                     for(int i = count; i < index; i++)
45                     {
46                         info.invokeAdd(seq, default(T));
47                     }
48                     info.invokeAdd(seq, v);
49                 }
50                 else
51                 {
52                     info.invokeSet(seq, index, v);
53                 }
54             };
55         }
56 
getInvokeInfo(Type t)57         private static InvokeInfo getInvokeInfo<T>(Type t)
58         {
59             lock(_methodTable)
60             {
61                 InvokeInfo i;
62                 if(_methodTable.TryGetValue(t, out i))
63                 {
64                     return i;
65                 }
66 
67                 MethodInfo am = t.GetMethod("Add", new Type[] { typeof(T) });
68                 if(am == null)
69                 {
70                     throw new Ice.MarshalException("Cannot patch a collection without an Add() method");
71                 }
72 
73                 PropertyInfo pi = t.GetProperty("Item");
74                 if(pi == null)
75                 {
76                     throw new Ice.MarshalException("Cannot patch a collection without an indexer");
77                 }
78                 MethodInfo sm = pi.GetSetMethod();
79                 if(sm == null)
80                 {
81                     throw new Ice.MarshalException("Cannot patch a collection without an indexer to set a value");
82                 }
83 
84                 pi = t.GetProperty("Count");
85                 if(pi == null)
86                 {
87                     throw new Ice.MarshalException("Cannot patch a collection without a Count property");
88                 }
89                 MethodInfo cm = pi.GetGetMethod();
90                 if(cm == null)
91                 {
92                     throw new Ice.MarshalException("Cannot patch a collection without a readable Count property");
93                 }
94 
95                 i = new InvokeInfo(am, sm, cm);
96                 _methodTable.Add(t, i);
97                 return i;
98             }
99         }
100 
101         private class InvokeInfo
102         {
InvokeInfo(MethodInfo am, MethodInfo sm, MethodInfo cm)103             public InvokeInfo(MethodInfo am, MethodInfo sm, MethodInfo cm)
104             {
105                 _addMethod = am;
106                 _setMethod = sm;
107                 _countMethod = cm;
108             }
109 
getCount(System.Collections.IEnumerable seq)110             internal int getCount(System.Collections.IEnumerable seq)
111             {
112                 try
113                 {
114                     return (int)_countMethod.Invoke(seq, null);
115                 }
116                 catch(Exception ex)
117                 {
118                     throw  new Ice.MarshalException("Could not read Count property during patching", ex);
119                 }
120             }
121 
invokeAdd(System.Collections.IEnumerable seq, object v)122             internal void invokeAdd(System.Collections.IEnumerable seq, object v)
123             {
124                 try
125                 {
126                     object[] arg = new object[] { v };
127                     _addMethod.Invoke(seq, arg);
128                 }
129                 catch(Exception ex)
130                 {
131                     throw  new Ice.MarshalException("Could not invoke Add method during patching", ex);
132                 }
133             }
134 
invokeSet(System.Collections.IEnumerable seq, int index, object v)135             internal void invokeSet(System.Collections.IEnumerable seq, int index, object v)
136             {
137                 try
138                 {
139                     object[] args = new object[] { index, v };
140                     _setMethod.Invoke(seq, args);
141                 }
142                 catch(Exception ex)
143                 {
144                     throw  new Ice.MarshalException("Could not call indexer during patching", ex);
145                 }
146             }
147 
148             private MethodInfo _addMethod;
149             private MethodInfo _setMethod;
150             private MethodInfo _countMethod;
151         }
152 
153         private static Dictionary<Type, InvokeInfo> _methodTable = new Dictionary<Type, InvokeInfo>();
154     }
155 }
156