// ********************************************************************** // // Copyright (c) 2003-2016 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. // // ********************************************************************** using System; using System.Collections.Generic; using System.Reflection; using System.Diagnostics; namespace IceInternal { public interface IPatcher { void patch(Ice.Object v); string type(); } public abstract class Patcher : IPatcher, Ice.ReadObjectCallback { public Patcher(string type) { _type = type; } public abstract void patch(Ice.Object v); public virtual string type() { return _type; } public virtual void invoke(Ice.Object v) { patch(v); } private string _type; } public sealed class ParamPatcher : Patcher { public ParamPatcher(string type) : base(type) { } public override void patch(Ice.Object v) { if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) { IceInternal.Ex.throwUOE(type(), v.ice_id()); } value = (T)v; } public T value; } public sealed class CustomSeqPatcher : Patcher { public CustomSeqPatcher(string type, IEnumerable seq, int index) : base(type) { _seq = seq; _seqType = seq.GetType(); _index = index; setInvokeInfo(_seqType); } public override void patch(Ice.Object v) { if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) { IceInternal.Ex.throwUOE(type(), v.ice_id()); } InvokeInfo info = getInvokeInfo(_seqType); int count = info.getCount(_seq); if(_index >= count) // Need to grow the sequence. { for(int i = count; i < _index; i++) { info.invokeAdd(_seq, default(T)); } info.invokeAdd(_seq, (T)v); } else { info.invokeSet(_seq, _index, (T)v); } } private static InvokeInfo getInvokeInfo(Type t) { lock(_methodTable) { try { return _methodTable[t]; } catch(KeyNotFoundException) { throw new Ice.MarshalException("No invoke record for type " + t.ToString()); } } } private static void setInvokeInfo(Type t) { lock(_methodTable) { if(_methodTable.ContainsKey(t)) { return; } MethodInfo am = t.GetMethod("Add", _params); if(am == null) { throw new Ice.MarshalException("Cannot patch a collection without an Add() method"); } PropertyInfo pi = t.GetProperty("Item"); if(pi == null) { throw new Ice.MarshalException("Cannot patch a collection without an indexer"); } MethodInfo sm = pi.GetSetMethod(); if(sm == null) { throw new Ice.MarshalException("Cannot patch a collection without an indexer to set a value"); } pi = t.GetProperty("Count"); if(pi == null) { throw new Ice.MarshalException("Cannot patch a collection without a Count property"); } MethodInfo cm = pi.GetGetMethod(); if(cm == null) { throw new Ice.MarshalException("Cannot patch a collection without a readable Count property"); } _methodTable.Add(t, new InvokeInfo(am, sm, cm)); } } private class InvokeInfo { public InvokeInfo(MethodInfo am, MethodInfo sm, MethodInfo cm) { _addMethod = am; _setMethod = sm; _countMethod = cm; } internal int getCount(System.Collections.IEnumerable seq) { try { return (int)_countMethod.Invoke(seq, null); } catch(Exception ex) { throw new Ice.MarshalException("Could not read Count property during patching", ex); } } internal void invokeAdd(System.Collections.IEnumerable seq, T v) { try { object[] arg = new object[] { v }; _addMethod.Invoke(seq, arg); } catch(Exception ex) { throw new Ice.MarshalException("Could not invoke Add method during patching", ex); } } internal void invokeSet(System.Collections.IEnumerable seq, int index, T v) { try { object[] args = new object[] { index, v }; _setMethod.Invoke(seq, args); } catch(Exception ex) { throw new Ice.MarshalException("Could not call indexer during patching", ex); } } private MethodInfo _addMethod; private MethodInfo _setMethod; private MethodInfo _countMethod; } private static Type[] _params = new Type[] { typeof(T) }; private static Dictionary _methodTable = new Dictionary(); private IEnumerable _seq; private Type _seqType; private int _index; // The index at which to patch the sequence. } public sealed class ArrayPatcher : Patcher { public ArrayPatcher(string type, T[] seq, int index) : base(type) { _seq = seq; _index = index; } public override void patch(Ice.Object v) { if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) { IceInternal.Ex.throwUOE(type(), v.ice_id()); } _seq[_index] = (T)v; } private T[] _seq; private int _index; // The index at which to patch the array. } public sealed class SequencePatcher : Patcher { public SequencePatcher(string type, IceInternal.CollectionBase seq, int index) : base(type) { _seq = seq; _index = index; } public override void patch(Ice.Object v) { if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) { IceInternal.Ex.throwUOE(type(), v.ice_id()); } int count = _seq.Count; if(_index >= count) // Need to grow the sequence. { for(int i = count; i < _index; i++) { _seq.Add(default(T)); } _seq.Add((T)v); } else { _seq[_index] = (T)v; } } private IceInternal.CollectionBase _seq; private int _index; // The index at which to patch the sequence. } public sealed class ListPatcher : Patcher { public ListPatcher(string type, List seq, int index) : base(type) { _seq = seq; _index = index; } public override void patch(Ice.Object v) { if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) { IceInternal.Ex.throwUOE(type(), v.ice_id()); } int count = _seq.Count; if(_index >= count) // Need to grow the sequence. { for(int i = count; i < _index; i++) { _seq.Add(default(T)); } _seq.Add((T)v); } else { _seq[_index] = (T)v; } } private List _seq; private int _index; // The index at which to patch the sequence. } }