http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableMode.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableMode.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableMode.cs new file mode 100644 index 0000000..670b091 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableMode.cs @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Portable +{ + /// <summary> + /// Portable mode. + /// </summary> + internal enum PortableMode + { + /// <summary> + /// Deserialize top-level portable objects, but leave nested portable objects in portable form. + /// </summary> + Deserialize, + + /// <summary> + /// Keep portable objects in portable form. + /// </summary> + KeepPortable, + + /// <summary> + /// Always return IPortableObject. + /// </summary> + ForcePortable + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableObjectHandle.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableObjectHandle.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableObjectHandle.cs new file mode 100644 index 0000000..f2c3842 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableObjectHandle.cs @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Portable +{ + /// <summary> + /// Object handle. Wraps a single value. + /// </summary> + internal class PortableObjectHandle + { + /** Value. */ + private readonly object _val; + + /// <summary> + /// Initializes a new instance of the <see cref="PortableObjectHandle"/> class. + /// </summary> + /// <param name="val">The value.</param> + public PortableObjectHandle(object val) + { + _val = val; + } + + /// <summary> + /// Gets the value. + /// </summary> + public object Value + { + get { return _val; } + } + + /** <inheritdoc /> */ + public override bool Equals(object obj) + { + var that = obj as PortableObjectHandle; + + return that != null && _val == that._val; + } + + /** <inheritdoc /> */ + public override int GetHashCode() + { + return _val != null ? _val.GetHashCode() : 0; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableOrSerializableObjectHolder.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableOrSerializableObjectHolder.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableOrSerializableObjectHolder.cs new file mode 100644 index 0000000..06ccf8b --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableOrSerializableObjectHolder.cs @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Portable +{ + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Wraps portable/serializable item in a portable. + /// </summary> + internal class PortableOrSerializableObjectHolder : IPortableWriteAware + { + /** */ + private readonly object _item; + + /// <summary> + /// Initializes a new instance of the <see cref="SerializableObjectHolder"/> class. + /// </summary> + /// <param name="item">The item to wrap.</param> + public PortableOrSerializableObjectHolder(object item) + { + _item = item; + } + + /// <summary> + /// Gets or sets the item to wrap. + /// </summary> + public object Item + { + get { return _item; } + } + + /** <inheritDoc /> */ + public void WritePortable(IPortableWriter writer) + { + var writer0 = (PortableWriterImpl)writer.RawWriter(); + + writer0.DetachNext(); + + PortableUtils.WritePortableOrSerializable(writer0, Item); + } + + /// <summary> + /// Initializes a new instance of the <see cref="PortableOrSerializableObjectHolder"/> class. + /// </summary> + /// <param name="reader">The reader.</param> + public PortableOrSerializableObjectHolder(IPortableReader reader) + { + _item = PortableUtils.ReadPortableOrSerializable<object>((PortableReaderImpl)reader.RawReader()); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderHandleDictionary.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderHandleDictionary.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderHandleDictionary.cs new file mode 100644 index 0000000..6a765c3 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderHandleDictionary.cs @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Portable +{ + /// <summary> + /// Object handle dictionary for PortableReader. + /// </summary> + internal class PortableReaderHandleDictionary : PortableHandleDictionary<int, object> + { + /// <summary> + /// Constructor with initial key-value pair. + /// </summary> + /// <param name="key">Key.</param> + /// <param name="val">Value.</param> + public PortableReaderHandleDictionary(int key, object val) + : base(key, val) + { + // No-op. + } + + /** <inheritdoc /> */ + protected override int EmptyKey + { + get { return -1; } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderImpl.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderImpl.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderImpl.cs new file mode 100644 index 0000000..176ca27 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderImpl.cs @@ -0,0 +1,1013 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Portable +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.IO; + using System.Runtime.Serialization; + using Apache.Ignite.Core.Impl.Common; + using Apache.Ignite.Core.Impl.Portable.IO; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Portable reader implementation. + /// </summary> + internal class PortableReaderImpl : IPortableReader, IPortableRawReader + { + /** Marshaller. */ + private readonly PortableMarshaller _marsh; + + /** Type descriptors. */ + private readonly IDictionary<long, IPortableTypeDescriptor> _descs; + + /** Parent builder. */ + private readonly PortableBuilderImpl _builder; + + /** Handles. */ + private PortableReaderHandleDictionary _hnds; + + /** Current type ID. */ + private int _curTypeId; + + /** Current position. */ + private int _curPos; + + /** Current raw data offset. */ + private int _curRawOffset; + + /** Current converter. */ + private IPortableNameMapper _curConverter; + + /** Current mapper. */ + private IPortableIdMapper _curMapper; + + /** Current raw flag. */ + private bool _curRaw; + + /** Detach flag. */ + private bool _detach; + + /** Portable read mode. */ + private PortableMode _mode; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="marsh">Marshaller.</param> + /// <param name="descs">Descriptors.</param> + /// <param name="stream">Input stream.</param> + /// <param name="mode">The mode.</param> + /// <param name="builder">Builder.</param> + public PortableReaderImpl + (PortableMarshaller marsh, + IDictionary<long, IPortableTypeDescriptor> descs, + IPortableStream stream, + PortableMode mode, + PortableBuilderImpl builder) + { + _marsh = marsh; + _descs = descs; + _mode = mode; + _builder = builder; + + Stream = stream; + } + + /// <summary> + /// Gets the marshaller. + /// </summary> + public PortableMarshaller Marshaller + { + get { return _marsh; } + } + + /** <inheritdoc /> */ + public IPortableRawReader RawReader() + { + MarkRaw(); + + return this; + } + + /** <inheritdoc /> */ + public bool ReadBoolean(string fieldName) + { + return ReadField(fieldName, r => r.ReadBoolean()); + } + + /** <inheritdoc /> */ + public bool ReadBoolean() + { + return Stream.ReadBool(); + } + + /** <inheritdoc /> */ + public bool[] ReadBooleanArray(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadBooleanArray); + } + + /** <inheritdoc /> */ + public bool[] ReadBooleanArray() + { + return Read(PortableUtils.ReadBooleanArray); + } + + /** <inheritdoc /> */ + public byte ReadByte(string fieldName) + { + return ReadField(fieldName, ReadByte); + } + + /** <inheritdoc /> */ + public byte ReadByte() + { + return Stream.ReadByte(); + } + + /** <inheritdoc /> */ + public byte[] ReadByteArray(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadByteArray); + } + + /** <inheritdoc /> */ + public byte[] ReadByteArray() + { + return Read(PortableUtils.ReadByteArray); + } + + /** <inheritdoc /> */ + public short ReadShort(string fieldName) + { + return ReadField(fieldName, ReadShort); + } + + /** <inheritdoc /> */ + public short ReadShort() + { + return Stream.ReadShort(); + } + + /** <inheritdoc /> */ + public short[] ReadShortArray(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadShortArray); + } + + /** <inheritdoc /> */ + public short[] ReadShortArray() + { + return Read(PortableUtils.ReadShortArray); + } + + /** <inheritdoc /> */ + public char ReadChar(string fieldName) + { + return ReadField(fieldName, ReadChar); + } + + /** <inheritdoc /> */ + public char ReadChar() + { + return Stream.ReadChar(); + } + + /** <inheritdoc /> */ + public char[] ReadCharArray(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadCharArray); + } + + /** <inheritdoc /> */ + public char[] ReadCharArray() + { + return Read(PortableUtils.ReadCharArray); + } + + /** <inheritdoc /> */ + public int ReadInt(string fieldName) + { + return ReadField(fieldName, ReadInt); + } + + /** <inheritdoc /> */ + public int ReadInt() + { + return Stream.ReadInt(); + } + + /** <inheritdoc /> */ + public int[] ReadIntArray(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadIntArray); + } + + /** <inheritdoc /> */ + public int[] ReadIntArray() + { + return Read(PortableUtils.ReadIntArray); + } + + /** <inheritdoc /> */ + public long ReadLong(string fieldName) + { + return ReadField(fieldName, ReadLong); + } + + /** <inheritdoc /> */ + public long ReadLong() + { + return Stream.ReadLong(); + } + + /** <inheritdoc /> */ + public long[] ReadLongArray(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadLongArray); + } + + /** <inheritdoc /> */ + public long[] ReadLongArray() + { + return Read(PortableUtils.ReadLongArray); + } + + /** <inheritdoc /> */ + public float ReadFloat(string fieldName) + { + return ReadField(fieldName, ReadFloat); + } + + /** <inheritdoc /> */ + public float ReadFloat() + { + return Stream.ReadFloat(); + } + + /** <inheritdoc /> */ + public float[] ReadFloatArray(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadFloatArray); + } + + /** <inheritdoc /> */ + public float[] ReadFloatArray() + { + return Read(PortableUtils.ReadFloatArray); + } + + /** <inheritdoc /> */ + public double ReadDouble(string fieldName) + { + return ReadField(fieldName, ReadDouble); + } + + /** <inheritdoc /> */ + public double ReadDouble() + { + return Stream.ReadDouble(); + } + + /** <inheritdoc /> */ + public double[] ReadDoubleArray(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadDoubleArray); + } + + /** <inheritdoc /> */ + public double[] ReadDoubleArray() + { + return Read(PortableUtils.ReadDoubleArray); + } + + /** <inheritdoc /> */ + public decimal ReadDecimal(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadDecimal); + } + + /** <inheritdoc /> */ + public decimal ReadDecimal() + { + return Read(PortableUtils.ReadDecimal); + } + + /** <inheritdoc /> */ + public decimal[] ReadDecimalArray(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadDecimalArray); + } + + /** <inheritdoc /> */ + public decimal[] ReadDecimalArray() + { + return Read(PortableUtils.ReadDecimalArray); + } + + /** <inheritdoc /> */ + public DateTime? ReadDate(string fieldName) + { + return ReadDate(fieldName, false); + } + + /** <inheritdoc /> */ + public DateTime? ReadDate(string fieldName, bool local) + { + return ReadField(fieldName, r => PortableUtils.ReadDate(r, local)); + } + + /** <inheritdoc /> */ + public DateTime? ReadDate() + { + return ReadDate(false); + } + + /** <inheritdoc /> */ + public DateTime? ReadDate(bool local) + { + return Read(r => PortableUtils.ReadDate(r, local)); + } + + /** <inheritdoc /> */ + public DateTime?[] ReadDateArray(string fieldName) + { + return ReadDateArray(fieldName, false); + } + + /** <inheritdoc /> */ + public DateTime?[] ReadDateArray(string fieldName, bool local) + { + return ReadField(fieldName, r => PortableUtils.ReadDateArray(r, local)); + } + + /** <inheritdoc /> */ + public DateTime?[] ReadDateArray() + { + return ReadDateArray(false); + } + + /** <inheritdoc /> */ + public DateTime?[] ReadDateArray(bool local) + { + return Read(r => PortableUtils.ReadDateArray(r, local)); + } + + /** <inheritdoc /> */ + public string ReadString(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadString); + } + + /** <inheritdoc /> */ + public string ReadString() + { + return Read(PortableUtils.ReadString); + } + + /** <inheritdoc /> */ + public string[] ReadStringArray(string fieldName) + { + return ReadField(fieldName, r => PortableUtils.ReadGenericArray<string>(r, false)); + } + + /** <inheritdoc /> */ + public string[] ReadStringArray() + { + return Read(r => PortableUtils.ReadGenericArray<string>(r, false)); + } + + /** <inheritdoc /> */ + public Guid? ReadGuid(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadGuid); + } + + /** <inheritdoc /> */ + public Guid? ReadGuid() + { + return Read(PortableUtils.ReadGuid); + } + + /** <inheritdoc /> */ + public Guid?[] ReadGuidArray(string fieldName) + { + return ReadField(fieldName, r => PortableUtils.ReadGenericArray<Guid?>(r, false)); + } + + /** <inheritdoc /> */ + public Guid?[] ReadGuidArray() + { + return Read(r => PortableUtils.ReadGenericArray<Guid?>(r, false)); + } + + /** <inheritdoc /> */ + public T ReadEnum<T>(string fieldName) + { + return ReadField(fieldName, PortableUtils.ReadEnum<T>); + } + + /** <inheritdoc /> */ + public T ReadEnum<T>() + { + return Read(PortableUtils.ReadEnum<T>); + } + + /** <inheritdoc /> */ + public T[] ReadEnumArray<T>(string fieldName) + { + return ReadField(fieldName, r => PortableUtils.ReadGenericArray<T>(r, true)); + } + + /** <inheritdoc /> */ + public T[] ReadEnumArray<T>() + { + return Read(r => PortableUtils.ReadGenericArray<T>(r, true)); + } + + /** <inheritdoc /> */ + public T ReadObject<T>(string fieldName) + { + if (_curRaw) + throw new PortableException("Cannot read named fields after raw data is read."); + + int fieldId = PortableUtils.FieldId(_curTypeId, fieldName, _curConverter, _curMapper); + + if (SeekField(fieldId)) + return Deserialize<T>(); + + return default(T); + } + + /** <inheritdoc /> */ + public T ReadObject<T>() + { + return Deserialize<T>(); + } + + /** <inheritdoc /> */ + public T[] ReadObjectArray<T>(string fieldName) + { + return ReadField(fieldName, r => PortableUtils.ReadGenericArray<T>(r, true)); + } + + /** <inheritdoc /> */ + public T[] ReadObjectArray<T>() + { + return Read(r => PortableUtils.ReadGenericArray<T>(r, true)); + } + + /** <inheritdoc /> */ + public ICollection ReadCollection(string fieldName) + { + return ReadCollection(fieldName, null, null); + } + + /** <inheritdoc /> */ + public ICollection ReadCollection() + { + return ReadCollection(null, null); + } + + /** <inheritdoc /> */ + public ICollection ReadCollection(string fieldName, PortableCollectionFactory factory, + PortableCollectionAdder adder) + { + return ReadField(fieldName, r => PortableUtils.ReadCollection(r, factory, adder)); + } + + /** <inheritdoc /> */ + public ICollection ReadCollection(PortableCollectionFactory factory, + PortableCollectionAdder adder) + { + return Read(r => PortableUtils.ReadCollection(r, factory, adder)); + } + + /** <inheritdoc /> */ + public ICollection<T> ReadGenericCollection<T>(string fieldName) + { + return ReadGenericCollection<T>(fieldName, null); + } + + /** <inheritdoc /> */ + public ICollection<T> ReadGenericCollection<T>() + { + return ReadGenericCollection((PortableGenericCollectionFactory<T>) null); + } + + /** <inheritdoc /> */ + public ICollection<T> ReadGenericCollection<T>(string fieldName, + PortableGenericCollectionFactory<T> factory) + { + return ReadField(fieldName, r => PortableUtils.ReadGenericCollection(r, factory)); + } + + /** <inheritdoc /> */ + public ICollection<T> ReadGenericCollection<T>(PortableGenericCollectionFactory<T> factory) + { + return Read(r => PortableUtils.ReadGenericCollection(r, factory)); + } + + /** <inheritdoc /> */ + public IDictionary ReadDictionary(string fieldName) + { + return ReadDictionary(fieldName, null); + } + + /** <inheritdoc /> */ + public IDictionary ReadDictionary() + { + return ReadDictionary((PortableDictionaryFactory)null); + } + + /** <inheritdoc /> */ + public IDictionary ReadDictionary(string fieldName, PortableDictionaryFactory factory) + { + return ReadField(fieldName, r => PortableUtils.ReadDictionary(r, factory)); + } + + /** <inheritdoc /> */ + public IDictionary ReadDictionary(PortableDictionaryFactory factory) + { + return Read(r => PortableUtils.ReadDictionary(r, factory)); + } + + /** <inheritdoc /> */ + public IDictionary<TK, TV> ReadGenericDictionary<TK, TV>(string fieldName) + { + return ReadGenericDictionary<TK, TV>(fieldName, null); + } + + /** <inheritdoc /> */ + public IDictionary<TK, TV> ReadGenericDictionary<TK, TV>() + { + return ReadGenericDictionary((PortableGenericDictionaryFactory<TK, TV>) null); + } + + /** <inheritdoc /> */ + public IDictionary<TK, TV> ReadGenericDictionary<TK, TV>(string fieldName, + PortableGenericDictionaryFactory<TK, TV> factory) + { + return ReadField(fieldName, r => PortableUtils.ReadGenericDictionary(r, factory)); + } + + /** <inheritdoc /> */ + public IDictionary<TK, TV> ReadGenericDictionary<TK, TV>(PortableGenericDictionaryFactory<TK, TV> factory) + { + return Read(r => PortableUtils.ReadGenericDictionary(r, factory)); + } + + /// <summary> + /// Enable detach mode for the next object read. + /// </summary> + public void DetachNext() + { + _detach = true; + } + + /// <summary> + /// Deserialize object. + /// </summary> + /// <returns>Deserialized object.</returns> + public T Deserialize<T>() + { + int pos = Stream.Position; + + byte hdr = Stream.ReadByte(); + + var doDetach = _detach; // save detach flag into a var and reset so it does not go deeper + + _detach = false; + + switch (hdr) + { + case PortableUtils.HdrNull: + return default(T); + + case PortableUtils.HdrHnd: + return ReadHandleObject<T>(pos); + + case PortableUtils.HdrFull: + return ReadFullObject<T>(pos); + + case PortableUtils.TypePortable: + return ReadPortableObject<T>(doDetach); + } + + if (PortableUtils.IsPredefinedType(hdr)) + return PortableSystemHandlers.ReadSystemType<T>(hdr, this); + + throw new PortableException("Invalid header on deserialization [pos=" + pos + ", hdr=" + hdr + ']'); + } + + /// <summary> + /// Reads the portable object. + /// </summary> + private T ReadPortableObject<T>(bool doDetach) + { + var len = Stream.ReadInt(); + + var portablePos = Stream.Position; + + if (_mode != PortableMode.Deserialize) + return TypeCaster<T>.Cast(ReadAsPortable(portablePos, len, doDetach)); + + Stream.Seek(len, SeekOrigin.Current); + + var offset = Stream.ReadInt(); + + var retPos = Stream.Position; + + Stream.Seek(portablePos + offset, SeekOrigin.Begin); + + _mode = PortableMode.KeepPortable; + + try + { + return Deserialize<T>(); + } + finally + { + _mode = PortableMode.Deserialize; + + Stream.Seek(retPos, SeekOrigin.Begin); + } + } + + /// <summary> + /// Reads the portable object in portable form. + /// </summary> + private PortableUserObject ReadAsPortable(int dataPos, int dataLen, bool doDetach) + { + try + { + Stream.Seek(dataLen + dataPos, SeekOrigin.Begin); + + var offs = Stream.ReadInt(); // offset inside data + + var pos = dataPos + offs; + + if (!doDetach) + return GetPortableUserObject(pos, pos, Stream.Array()); + + Stream.Seek(pos + 10, SeekOrigin.Begin); + + var len = Stream.ReadInt(); + + Stream.Seek(pos, SeekOrigin.Begin); + + return GetPortableUserObject(pos, 0, Stream.ReadByteArray(len)); + } + finally + { + Stream.Seek(dataPos + dataLen + 4, SeekOrigin.Begin); + } + } + + /// <summary> + /// Reads the full object. + /// </summary> + [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "hashCode")] + private T ReadFullObject<T>(int pos) + { + // Read header. + bool userType = Stream.ReadBool(); + int typeId = Stream.ReadInt(); + // ReSharper disable once UnusedVariable + int hashCode = Stream.ReadInt(); + int len = Stream.ReadInt(); + int rawOffset = Stream.ReadInt(); + + try + { + // Already read this object? + object hndObj; + + if (_hnds != null && _hnds.TryGetValue(pos, out hndObj)) + return (T) hndObj; + + if (userType && _mode == PortableMode.ForcePortable) + { + PortableUserObject portObj; + + if (_detach) + { + Stream.Seek(pos, SeekOrigin.Begin); + + portObj = GetPortableUserObject(pos, 0, Stream.ReadByteArray(len)); + } + else + portObj = GetPortableUserObject(pos, pos, Stream.Array()); + + T obj = _builder == null ? TypeCaster<T>.Cast(portObj) : TypeCaster<T>.Cast(_builder.Child(portObj)); + + AddHandle(pos, obj); + + return obj; + } + else + { + // Find descriptor. + IPortableTypeDescriptor desc; + + if (!_descs.TryGetValue(PortableUtils.TypeKey(userType, typeId), out desc)) + throw new PortableException("Unknown type ID: " + typeId); + + // Instantiate object. + if (desc.Type == null) + throw new PortableException("No matching type found for object [typeId=" + + desc.TypeId + ", typeName=" + desc.TypeName + ']'); + + // Preserve old frame. + int oldTypeId = _curTypeId; + int oldPos = _curPos; + int oldRawOffset = _curRawOffset; + IPortableNameMapper oldConverter = _curConverter; + IPortableIdMapper oldMapper = _curMapper; + bool oldRaw = _curRaw; + + // Set new frame. + _curTypeId = typeId; + _curPos = pos; + _curRawOffset = rawOffset; + _curConverter = desc.NameConverter; + _curMapper = desc.Mapper; + _curRaw = false; + + // Read object. + object obj; + + var sysSerializer = desc.Serializer as IPortableSystemTypeSerializer; + + if (sysSerializer != null) + obj = sysSerializer.ReadInstance(this); + else + { + try + { + obj = FormatterServices.GetUninitializedObject(desc.Type); + + // Save handle. + AddHandle(pos, obj); + } + catch (Exception e) + { + throw new PortableException("Failed to create type instance: " + + desc.Type.AssemblyQualifiedName, e); + } + + desc.Serializer.ReadPortable(obj, this); + } + + // Restore old frame. + _curTypeId = oldTypeId; + _curPos = oldPos; + _curRawOffset = oldRawOffset; + _curConverter = oldConverter; + _curMapper = oldMapper; + _curRaw = oldRaw; + + var wrappedSerializable = obj as SerializableObjectHolder; + + return wrappedSerializable != null ? (T) wrappedSerializable.Item : (T) obj; + } + } + finally + { + // Advance stream pointer. + Stream.Seek(pos + len, SeekOrigin.Begin); + } + } + + /// <summary> + /// Reads the handle object. + /// </summary> + private T ReadHandleObject<T>(int pos) + { + // Get handle position. + int hndPos = pos - Stream.ReadInt(); + + int retPos = Stream.Position; + + try + { + object hndObj; + + if (_builder == null || !_builder.CachedField(hndPos, out hndObj)) + { + if (_hnds == null || !_hnds.TryGetValue(hndPos, out hndObj)) + { + // No such handler, i.e. we trying to deserialize inner object before deserializing outer. + Stream.Seek(hndPos, SeekOrigin.Begin); + + hndObj = Deserialize<T>(); + } + + // Notify builder that we deserialized object on other location. + if (_builder != null) + _builder.CacheField(hndPos, hndObj); + } + + return (T) hndObj; + } + finally + { + // Position stream to correct place. + Stream.Seek(retPos, SeekOrigin.Begin); + } + } + + /// <summary> + /// Adds a handle to the dictionary. + /// </summary> + /// <param name="pos">Position.</param> + /// <param name="obj">Object.</param> + private void AddHandle(int pos, object obj) + { + if (_hnds == null) + _hnds = new PortableReaderHandleDictionary(pos, obj); + else + _hnds.Add(pos, obj); + } + + /// <summary> + /// Underlying stream. + /// </summary> + public IPortableStream Stream + { + get; + private set; + } + + /// <summary> + /// Mark current output as raw. + /// </summary> + private void MarkRaw() + { + if (!_curRaw) + { + _curRaw = true; + + Stream.Seek(_curPos + _curRawOffset, SeekOrigin.Begin); + } + } + + /// <summary> + /// Seek field with the given ID in the current object. + /// </summary> + /// <param name="fieldId">Field ID.</param> + /// <returns>True in case the field was found and position adjusted, false otherwise.</returns> + private bool SeekField(int fieldId) + { + // This method is expected to be called when stream pointer is set either before + // the field or on raw data offset. + int start = _curPos + 18; + int end = _curPos + _curRawOffset; + + int initial = Stream.Position; + + int cur = initial; + + while (cur < end) + { + int id = Stream.ReadInt(); + + if (fieldId == id) + { + // Field is found, return. + Stream.Seek(4, SeekOrigin.Current); + + return true; + } + + Stream.Seek(Stream.ReadInt(), SeekOrigin.Current); + + cur = Stream.Position; + } + + Stream.Seek(start, SeekOrigin.Begin); + + cur = start; + + while (cur < initial) + { + int id = Stream.ReadInt(); + + if (fieldId == id) + { + // Field is found, return. + Stream.Seek(4, SeekOrigin.Current); + + return true; + } + + Stream.Seek(Stream.ReadInt(), SeekOrigin.Current); + + cur = Stream.Position; + } + + return false; + } + + /// <summary> + /// Determines whether header at current position is HDR_NULL. + /// </summary> + private bool IsNullHeader() + { + var hdr = ReadByte(); + + return hdr != PortableUtils.HdrNull; + } + + /// <summary> + /// Seeks the field by name, reads header and returns true if field is present and header is not null. + /// </summary> + private bool SeekField(string fieldName) + { + if (_curRaw) + throw new PortableException("Cannot read named fields after raw data is read."); + + var fieldId = PortableUtils.FieldId(_curTypeId, fieldName, _curConverter, _curMapper); + + if (!SeekField(fieldId)) + return false; + + return IsNullHeader(); + } + + /// <summary> + /// Seeks specified field and invokes provided func. + /// </summary> + private T ReadField<T>(string fieldName, Func<IPortableStream, T> readFunc) + { + return SeekField(fieldName) ? readFunc(Stream) : default(T); + } + + /// <summary> + /// Seeks specified field and invokes provided func. + /// </summary> + private T ReadField<T>(string fieldName, Func<PortableReaderImpl, T> readFunc) + { + return SeekField(fieldName) ? readFunc(this) : default(T); + } + + /// <summary> + /// Seeks specified field and invokes provided func. + /// </summary> + private T ReadField<T>(string fieldName, Func<T> readFunc) + { + return SeekField(fieldName) ? readFunc() : default(T); + } + + /// <summary> + /// Reads header and invokes specified func if the header is not null. + /// </summary> + private T Read<T>(Func<PortableReaderImpl, T> readFunc) + { + return IsNullHeader() ? readFunc(this) : default(T); + } + + /// <summary> + /// Reads header and invokes specified func if the header is not null. + /// </summary> + private T Read<T>(Func<IPortableStream, T> readFunc) + { + return IsNullHeader() ? readFunc(Stream) : default(T); + } + + /// <summary> + /// Gets the portable user object from a byte array. + /// </summary> + /// <param name="pos">Position in the current stream.</param> + /// <param name="offs">Offset in the byte array.</param> + /// <param name="bytes">Bytes.</param> + private PortableUserObject GetPortableUserObject(int pos, int offs, byte[] bytes) + { + Stream.Seek(pos + 2, SeekOrigin.Begin); + + var id = Stream.ReadInt(); + + var hash = Stream.ReadInt(); + + return new PortableUserObject(_marsh, bytes, offs, id, hash); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReflectiveRoutines.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReflectiveRoutines.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReflectiveRoutines.cs new file mode 100644 index 0000000..d939d29 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReflectiveRoutines.cs @@ -0,0 +1,483 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Portable +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Linq.Expressions; + using System.Reflection; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Impl.Common; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Write action delegate. + /// </summary> + /// <param name="obj">Target object.</param> + /// <param name="writer">Writer.</param> + internal delegate void PortableReflectiveWriteAction(object obj, IPortableWriter writer); + + /// <summary> + /// Read action delegate. + /// </summary> + /// <param name="obj">Target object.</param> + /// <param name="reader">Reader.</param> + internal delegate void PortableReflectiveReadAction(object obj, IPortableReader reader); + + /// <summary> + /// Routines for reflective reads and writes. + /// </summary> + internal static class PortableReflectiveActions + { + /** Method: read enum. */ + private static readonly MethodInfo MthdReadEnum = + typeof(IPortableReader).GetMethod("ReadEnum", new[] { typeof(string) }); + + /** Method: read enum array. */ + private static readonly MethodInfo MthdReadEnumArray = + typeof(IPortableReader).GetMethod("ReadEnumArray", new[] { typeof(string) }); + + /** Method: read array. */ + private static readonly MethodInfo MthdReadObjArray = + typeof(IPortableReader).GetMethod("ReadObjectArray", new[] { typeof(string) }); + + /** Method: read generic collection. */ + private static readonly MethodInfo MthdReadGenericCollection = + typeof(IPortableReader).GetMethod("ReadGenericCollection", new[] { typeof(string) }); + + /** Method: read generic dictionary. */ + private static readonly MethodInfo MthdReadGenericDictionary = + typeof(IPortableReader).GetMethod("ReadGenericDictionary", new[] { typeof(string) }); + + /** Method: read object. */ + private static readonly MethodInfo MthdReadObj= + typeof(IPortableReader).GetMethod("ReadObject", new[] { typeof(string) }); + + /** Method: write enum array. */ + private static readonly MethodInfo MthdWriteEnumArray = + typeof(IPortableWriter).GetMethod("WriteEnumArray"); + + /** Method: write array. */ + private static readonly MethodInfo MthdWriteObjArray = + typeof(IPortableWriter).GetMethod("WriteObjectArray"); + + /** Method: write generic collection. */ + private static readonly MethodInfo MthdWriteGenericCollection = + typeof(IPortableWriter).GetMethod("WriteGenericCollection"); + + /** Method: write generic dictionary. */ + private static readonly MethodInfo MthdWriteGenericDictionary = + typeof(IPortableWriter).GetMethod("WriteGenericDictionary"); + + /** Method: read object. */ + private static readonly MethodInfo MthdWriteObj = + typeof(IPortableWriter).GetMethod("WriteObject"); + + /// <summary> + /// Lookup read/write actions for the given type. + /// </summary> + /// <param name="field">The field.</param> + /// <param name="writeAction">Write action.</param> + /// <param name="readAction">Read action.</param> + public static void TypeActions(FieldInfo field, out PortableReflectiveWriteAction writeAction, + out PortableReflectiveReadAction readAction) + { + var type = field.FieldType; + + if (type.IsPrimitive) + HandlePrimitive(field, out writeAction, out readAction); + else if (type.IsArray) + HandleArray(field, out writeAction, out readAction); + else + HandleOther(field, out writeAction, out readAction); + } + + /// <summary> + /// Handle primitive type. + /// </summary> + /// <param name="field">The field.</param> + /// <param name="writeAction">Write action.</param> + /// <param name="readAction">Read action.</param> + /// <exception cref="IgniteException">Unsupported primitive type: + type.Name</exception> + private static void HandlePrimitive(FieldInfo field, out PortableReflectiveWriteAction writeAction, + out PortableReflectiveReadAction readAction) + { + var type = field.FieldType; + + if (type == typeof(bool)) + { + writeAction = GetWriter<bool>(field, (f, w, o) => w.WriteBoolean(f, o)); + readAction = GetReader(field, (f, r) => r.ReadBoolean(f)); + } + else if (type == typeof(sbyte)) + { + writeAction = GetWriter<sbyte>(field, (f, w, o) => w.WriteByte(f, unchecked((byte) o))); + readAction = GetReader(field, (f, r) => unchecked ((sbyte)r.ReadByte(f))); + } + else if (type == typeof(byte)) + { + writeAction = GetWriter<byte>(field, (f, w, o) => w.WriteByte(f, o)); + readAction = GetReader(field, (f, r) => r.ReadByte(f)); + } + else if (type == typeof(short)) + { + writeAction = GetWriter<short>(field, (f, w, o) => w.WriteShort(f, o)); + readAction = GetReader(field, (f, r) => r.ReadShort(f)); + } + else if (type == typeof(ushort)) + { + writeAction = GetWriter<ushort>(field, (f, w, o) => w.WriteShort(f, unchecked((short) o))); + readAction = GetReader(field, (f, r) => unchecked((ushort) r.ReadShort(f))); + } + else if (type == typeof(char)) + { + writeAction = GetWriter<char>(field, (f, w, o) => w.WriteChar(f, o)); + readAction = GetReader(field, (f, r) => r.ReadChar(f)); + } + else if (type == typeof(int)) + { + writeAction = GetWriter<int>(field, (f, w, o) => w.WriteInt(f, o)); + readAction = GetReader(field, (f, r) => r.ReadInt(f)); + } + else if (type == typeof(uint)) + { + writeAction = GetWriter<uint>(field, (f, w, o) => w.WriteInt(f, unchecked((int) o))); + readAction = GetReader(field, (f, r) => unchecked((uint) r.ReadInt(f))); + } + else if (type == typeof(long)) + { + writeAction = GetWriter<long>(field, (f, w, o) => w.WriteLong(f, o)); + readAction = GetReader(field, (f, r) => r.ReadLong(f)); + } + else if (type == typeof(ulong)) + { + writeAction = GetWriter<ulong>(field, (f, w, o) => w.WriteLong(f, unchecked((long) o))); + readAction = GetReader(field, (f, r) => unchecked((ulong) r.ReadLong(f))); + } + else if (type == typeof(float)) + { + writeAction = GetWriter<float>(field, (f, w, o) => w.WriteFloat(f, o)); + readAction = GetReader(field, (f, r) => r.ReadFloat(f)); + } + else if (type == typeof(double)) + { + writeAction = GetWriter<double>(field, (f, w, o) => w.WriteDouble(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDouble(f)); + } + else + throw new IgniteException("Unsupported primitive type: " + type.Name); + } + + /// <summary> + /// Handle array type. + /// </summary> + /// <param name="field">The field.</param> + /// <param name="writeAction">Write action.</param> + /// <param name="readAction">Read action.</param> + private static void HandleArray(FieldInfo field, out PortableReflectiveWriteAction writeAction, + out PortableReflectiveReadAction readAction) + { + Type elemType = field.FieldType.GetElementType(); + + if (elemType == typeof(bool)) + { + writeAction = GetWriter<bool[]>(field, (f, w, o) => w.WriteBooleanArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadBooleanArray(f)); + } + else if (elemType == typeof(byte)) + { + writeAction = GetWriter<byte[]>(field, (f, w, o) => w.WriteByteArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadByteArray(f)); + } + else if (elemType == typeof(sbyte)) + { + writeAction = GetWriter<sbyte[]>(field, (f, w, o) => w.WriteByteArray(f, (byte[]) (Array) o)); + readAction = GetReader(field, (f, r) => (sbyte[]) (Array) r.ReadByteArray(f)); + } + else if (elemType == typeof(short)) + { + writeAction = GetWriter<short[]>(field, (f, w, o) => w.WriteShortArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadShortArray(f)); + } + else if (elemType == typeof(ushort)) + { + writeAction = GetWriter<ushort[]>(field, (f, w, o) => w.WriteShortArray(f, (short[]) (Array) o)); + readAction = GetReader(field, (f, r) => (ushort[]) (Array) r.ReadShortArray(f)); + } + else if (elemType == typeof(char)) + { + writeAction = GetWriter<char[]>(field, (f, w, o) => w.WriteCharArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadCharArray(f)); + } + else if (elemType == typeof(int)) + { + writeAction = GetWriter<int[]>(field, (f, w, o) => w.WriteIntArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadIntArray(f)); + } + else if (elemType == typeof(uint)) + { + writeAction = GetWriter<uint[]>(field, (f, w, o) => w.WriteIntArray(f, (int[]) (Array) o)); + readAction = GetReader(field, (f, r) => (uint[]) (Array) r.ReadIntArray(f)); + } + else if (elemType == typeof(long)) + { + writeAction = GetWriter<long[]>(field, (f, w, o) => w.WriteLongArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadLongArray(f)); + } + else if (elemType == typeof(ulong)) + { + writeAction = GetWriter<ulong[]>(field, (f, w, o) => w.WriteLongArray(f, (long[]) (Array) o)); + readAction = GetReader(field, (f, r) => (ulong[]) (Array) r.ReadLongArray(f)); + } + else if (elemType == typeof(float)) + { + writeAction = GetWriter<float[]>(field, (f, w, o) => w.WriteFloatArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadFloatArray(f)); + } + else if (elemType == typeof(double)) + { + writeAction = GetWriter<double[]>(field, (f, w, o) => w.WriteDoubleArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDoubleArray(f)); + } + else if (elemType == typeof(decimal)) + { + writeAction = GetWriter<decimal[]>(field, (f, w, o) => w.WriteDecimalArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDecimalArray(f)); + } + else if (elemType == typeof(string)) + { + writeAction = GetWriter<string[]>(field, (f, w, o) => w.WriteStringArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadStringArray(f)); + } + else if (elemType == typeof(Guid?)) + { + writeAction = GetWriter<Guid?[]>(field, (f, w, o) => w.WriteGuidArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadGuidArray(f)); + } + else if (elemType == typeof(DateTime?)) + { + writeAction = GetWriter<DateTime?[]>(field, (f, w, o) => w.WriteDateArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDateArray(f)); + } + else if (elemType.IsEnum) + { + writeAction = GetWriter(field, MthdWriteEnumArray, elemType); + readAction = GetReader(field, MthdReadEnumArray, elemType); + } + else + { + writeAction = GetWriter(field, MthdWriteObjArray, elemType); + readAction = GetReader(field, MthdReadObjArray, elemType); + } + } + + /// <summary> + /// Handle other type. + /// </summary> + /// <param name="field">The field.</param> + /// <param name="writeAction">Write action.</param> + /// <param name="readAction">Read action.</param> + private static void HandleOther(FieldInfo field, out PortableReflectiveWriteAction writeAction, + out PortableReflectiveReadAction readAction) + { + var type = field.FieldType; + + var genericDef = type.IsGenericType ? type.GetGenericTypeDefinition() : null; + + bool nullable = genericDef == typeof(Nullable<>); + + var nullableType = nullable ? type.GetGenericArguments()[0] : null; + + if (type == typeof(decimal)) + { + writeAction = GetWriter<decimal>(field, (f, w, o) => w.WriteDecimal(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDecimal(f)); + } + else if (type == typeof(string)) + { + writeAction = GetWriter<string>(field, (f, w, o) => w.WriteString(f, o)); + readAction = GetReader(field, (f, r) => r.ReadString(f)); + } + else if (type == typeof(Guid)) + { + writeAction = GetWriter<Guid>(field, (f, w, o) => w.WriteGuid(f, o)); + readAction = GetReader(field, (f, r) => r.ReadGuid(f) ?? default(Guid)); + } + else if (nullable && nullableType == typeof(Guid)) + { + writeAction = GetWriter<Guid?>(field, (f, w, o) => w.WriteGuid(f, o)); + readAction = GetReader(field, (f, r) => r.ReadGuid(f)); + } + else if (type == typeof(DateTime)) + { + writeAction = GetWriter<DateTime>(field, (f, w, o) => w.WriteDate(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDate(f) ?? default(DateTime)); + } + else if (nullable && nullableType == typeof(DateTime)) + { + writeAction = GetWriter<DateTime?>(field, (f, w, o) => w.WriteDate(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDate(f)); + } + else if (type.IsEnum) + { + writeAction = GetWriter<object>(field, (f, w, o) => w.WriteEnum(f, o), true); + readAction = GetReader(field, MthdReadEnum); + } + else if (genericDef == PortableUtils.TypGenericDictionary || + type.GetInterface(PortableUtils.TypGenericDictionary.FullName) != null) + { + writeAction = GetWriter(field, MthdWriteGenericDictionary, type.GetGenericArguments()); + readAction = GetReader(field, MthdReadGenericDictionary, type.GetGenericArguments()); + } + else if (genericDef == PortableUtils.TypGenericCollection || + type.GetInterface(PortableUtils.TypGenericCollection.FullName) != null) + { + writeAction = GetWriter(field, MthdWriteGenericCollection, type.GetGenericArguments()); + readAction = GetReader(field, MthdReadGenericCollection, type.GetGenericArguments()); + } + else if (type == PortableUtils.TypDictionary || type.GetInterface(PortableUtils.TypDictionary.FullName) != null) + { + writeAction = GetWriter<IDictionary>(field, (f, w, o) => w.WriteDictionary(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDictionary(f)); + } + else if (type == PortableUtils.TypCollection || type.GetInterface(PortableUtils.TypCollection.FullName) != null) + { + writeAction = GetWriter<ICollection>(field, (f, w, o) => w.WriteCollection(f, o)); + readAction = GetReader(field, (f, r) => r.ReadCollection(f)); + } + else + { + writeAction = GetWriter(field, MthdWriteObj); + readAction = GetReader(field, MthdReadObj); + } + } + + /// <summary> + /// Gets the reader with a specified write action. + /// </summary> + private static PortableReflectiveWriteAction GetWriter<T>(FieldInfo field, + Expression<Action<string, IPortableWriter, T>> write, + bool convertFieldValToObject = false) + { + Debug.Assert(field != null); + Debug.Assert(field.DeclaringType != null); // non-static + + // Get field value + var targetParam = Expression.Parameter(typeof(object)); + var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType); + Expression fldExpr = Expression.Field(targetParamConverted, field); + + if (convertFieldValToObject) + fldExpr = Expression.Convert(fldExpr, typeof (object)); + + // Call IPortableWriter method + var writerParam = Expression.Parameter(typeof(IPortableWriter)); + var fldNameParam = Expression.Constant(PortableUtils.CleanFieldName(field.Name)); + var writeExpr = Expression.Invoke(write, fldNameParam, writerParam, fldExpr); + + // Compile and return + return Expression.Lambda<PortableReflectiveWriteAction>(writeExpr, targetParam, writerParam).Compile(); + } + + /// <summary> + /// Gets the writer with a specified generic method. + /// </summary> + private static PortableReflectiveWriteAction GetWriter(FieldInfo field, MethodInfo method, + params Type[] genericArgs) + { + Debug.Assert(field != null); + Debug.Assert(field.DeclaringType != null); // non-static + + if (genericArgs.Length == 0) + genericArgs = new[] {field.FieldType}; + + // Get field value + var targetParam = Expression.Parameter(typeof(object)); + var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType); + var fldExpr = Expression.Field(targetParamConverted, field); + + // Call IPortableWriter method + var writerParam = Expression.Parameter(typeof(IPortableWriter)); + var fldNameParam = Expression.Constant(PortableUtils.CleanFieldName(field.Name)); + var writeMethod = method.MakeGenericMethod(genericArgs); + var writeExpr = Expression.Call(writerParam, writeMethod, fldNameParam, fldExpr); + + // Compile and return + return Expression.Lambda<PortableReflectiveWriteAction>(writeExpr, targetParam, writerParam).Compile(); + } + + /// <summary> + /// Gets the reader with a specified read action. + /// </summary> + private static PortableReflectiveReadAction GetReader<T>(FieldInfo field, + Expression<Func<string, IPortableReader, T>> read) + { + Debug.Assert(field != null); + Debug.Assert(field.DeclaringType != null); // non-static + + // Call IPortableReader method + var readerParam = Expression.Parameter(typeof(IPortableReader)); + var fldNameParam = Expression.Constant(PortableUtils.CleanFieldName(field.Name)); + Expression readExpr = Expression.Invoke(read, fldNameParam, readerParam); + + if (typeof(T) != field.FieldType) + readExpr = Expression.Convert(readExpr, field.FieldType); + + // Assign field value + var targetParam = Expression.Parameter(typeof(object)); + var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType); + var assignExpr = Expression.Call(DelegateConverter.GetWriteFieldMethod(field), targetParamConverted, + readExpr); + + // Compile and return + return Expression.Lambda<PortableReflectiveReadAction>(assignExpr, targetParam, readerParam).Compile(); + } + + /// <summary> + /// Gets the reader with a specified generic method. + /// </summary> + private static PortableReflectiveReadAction GetReader(FieldInfo field, MethodInfo method, + params Type[] genericArgs) + { + Debug.Assert(field != null); + Debug.Assert(field.DeclaringType != null); // non-static + + if (genericArgs.Length == 0) + genericArgs = new[] {field.FieldType}; + + // Call IPortableReader method + var readerParam = Expression.Parameter(typeof (IPortableReader)); + var fldNameParam = Expression.Constant(PortableUtils.CleanFieldName(field.Name)); + var readMethod = method.MakeGenericMethod(genericArgs); + Expression readExpr = Expression.Call(readerParam, readMethod, fldNameParam); + + if (readMethod.ReturnType != field.FieldType) + readExpr = Expression.Convert(readExpr, field.FieldType); + + // Assign field value + var targetParam = Expression.Parameter(typeof(object)); + var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType); + var assignExpr = Expression.Call(DelegateConverter.GetWriteFieldMethod(field), targetParamConverted, + readExpr); + + // Compile and return + return Expression.Lambda<PortableReflectiveReadAction>(assignExpr, targetParam, readerParam).Compile(); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReflectiveSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReflectiveSerializer.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReflectiveSerializer.cs new file mode 100644 index 0000000..3dff691 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReflectiveSerializer.cs @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Portable +{ + using System; + using System.Collections.Generic; + using System.Reflection; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Portable serializer which reflectively writes all fields except of ones with + /// <see cref="System.NonSerializedAttribute"/>. + /// <para /> + /// Note that Java platform stores dates as a difference between current time + /// and predefined absolute UTC date. Therefore, this difference is always the + /// same for all time zones. .Net, in contrast, stores dates as a difference + /// between current time and some predefined date relative to the current time + /// zone. It means that this difference will be different as you change time zones. + /// To overcome this discrepancy Ignite always converts .Net date to UTC form + /// before serializing and allows user to decide whether to deserialize them + /// in UTC or local form using <c>ReadDate(..., true/false)</c> methods in + /// <see cref="IPortableReader"/> and <see cref="IPortableRawReader"/>. + /// This serializer always read dates in UTC form. It means that if you have + /// local date in any field/property, it will be implicitly converted to UTC + /// form after the first serialization-deserialization cycle. + /// </summary> + internal class PortableReflectiveSerializer : IPortableSerializer + { + /** Cached binding flags. */ + private static readonly BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | + BindingFlags.NonPublic | BindingFlags.DeclaredOnly; + + /** Cached type descriptors. */ + private readonly IDictionary<Type, Descriptor> _types = new Dictionary<Type, Descriptor>(); + + /// <summary> + /// Write portalbe object. + /// </summary> + /// <param name="obj">Object.</param> + /// <param name="writer">Portable writer.</param> + /// <exception cref="PortableException">Type is not registered in serializer: + type.Name</exception> + public void WritePortable(object obj, IPortableWriter writer) + { + var portableMarshalAware = obj as IPortableMarshalAware; + + if (portableMarshalAware != null) + portableMarshalAware.WritePortable(writer); + else + GetDescriptor(obj).Write(obj, writer); + } + + /// <summary> + /// Read portable object. + /// </summary> + /// <param name="obj">Instantiated empty object.</param> + /// <param name="reader">Portable reader.</param> + /// <exception cref="PortableException">Type is not registered in serializer: + type.Name</exception> + public void ReadPortable(object obj, IPortableReader reader) + { + var portableMarshalAware = obj as IPortableMarshalAware; + + if (portableMarshalAware != null) + portableMarshalAware.ReadPortable(reader); + else + GetDescriptor(obj).Read(obj, reader); + } + + /// <summary>Register type.</summary> + /// <param name="type">Type.</param> + /// <param name="typeId">Type ID.</param> + /// <param name="converter">Name converter.</param> + /// <param name="idMapper">ID mapper.</param> + public void Register(Type type, int typeId, IPortableNameMapper converter, + IPortableIdMapper idMapper) + { + if (type.GetInterface(typeof(IPortableMarshalAware).Name) != null) + return; + + List<FieldInfo> fields = new List<FieldInfo>(); + + Type curType = type; + + while (curType != null) + { + foreach (FieldInfo field in curType.GetFields(Flags)) + { + if (!field.IsNotSerialized) + fields.Add(field); + } + + curType = curType.BaseType; + } + + IDictionary<int, string> idMap = new Dictionary<int, string>(); + + foreach (FieldInfo field in fields) + { + string fieldName = PortableUtils.CleanFieldName(field.Name); + + int fieldId = PortableUtils.FieldId(typeId, fieldName, converter, idMapper); + + if (idMap.ContainsKey(fieldId)) + { + throw new PortableException("Conflicting field IDs [type=" + + type.Name + ", field1=" + idMap[fieldId] + ", field2=" + fieldName + + ", fieldId=" + fieldId + ']'); + } + + idMap[fieldId] = fieldName; + } + + fields.Sort(Compare); + + Descriptor desc = new Descriptor(fields); + + _types[type] = desc; + } + + /// <summary> + /// Gets the descriptor for an object. + /// </summary> + private Descriptor GetDescriptor(object obj) + { + var type = obj.GetType(); + + Descriptor desc; + + if (!_types.TryGetValue(type, out desc)) + throw new PortableException("Type is not registered in serializer: " + type.Name); + + return desc; + } + + /// <summary> + /// Compare two FieldInfo instances. + /// </summary> + private static int Compare(FieldInfo info1, FieldInfo info2) { + string name1 = PortableUtils.CleanFieldName(info1.Name); + string name2 = PortableUtils.CleanFieldName(info2.Name); + + return string.Compare(name1, name2, StringComparison.OrdinalIgnoreCase); + } + + /// <summary> + /// Type descriptor. + /// </summary> + private class Descriptor + { + /** Write actions to be performed. */ + private readonly List<PortableReflectiveWriteAction> _wActions; + + /** Read actions to be performed. */ + private readonly List<PortableReflectiveReadAction> _rActions; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="fields">Fields.</param> + public Descriptor(List<FieldInfo> fields) + { + _wActions = new List<PortableReflectiveWriteAction>(fields.Count); + _rActions = new List<PortableReflectiveReadAction>(fields.Count); + + foreach (FieldInfo field in fields) + { + PortableReflectiveWriteAction writeAction; + PortableReflectiveReadAction readAction; + + PortableReflectiveActions.TypeActions(field, out writeAction, out readAction); + + _wActions.Add(writeAction); + _rActions.Add(readAction); + } + } + + /// <summary> + /// Write object. + /// </summary> + /// <param name="obj">Object.</param> + /// <param name="writer">Portable writer.</param> + public void Write(object obj, IPortableWriter writer) + { + int cnt = _wActions.Count; + + for (int i = 0; i < cnt; i++) + _wActions[i](obj, writer); + } + + /// <summary> + /// Read object. + /// </summary> + /// <param name="obj">Object.</param> + /// <param name="reader">Portable reader.</param> + public void Read(object obj, IPortableReader reader) + { + int cnt = _rActions.Count; + + for (int i = 0; i < cnt; i++ ) + _rActions[i](obj, reader); + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs new file mode 100644 index 0000000..c8dcc5a --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Impl.Portable +{ + using System; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Surrogate type descriptor. Used in cases when type if identified by name and is not provided in configuration. + /// </summary> + internal class PortableSurrogateTypeDescriptor : IPortableTypeDescriptor + { + /** Portable configuration. */ + private readonly PortableConfiguration _cfg; + + /** Type ID. */ + private readonly int _id; + + /** Type name. */ + private readonly string _name; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="cfg">Portable configuration.</param> + /// <param name="id">Type ID.</param> + public PortableSurrogateTypeDescriptor(PortableConfiguration cfg, int id) + { + _cfg = cfg; + _id = id; + } + + /// <summary> + /// Constrcutor. + /// </summary> + /// <param name="cfg">Portable configuration.</param> + /// <param name="name">Type name.</param> + public PortableSurrogateTypeDescriptor(PortableConfiguration cfg, string name) + { + _cfg = cfg; + _name = name; + + _id = PortableUtils.TypeId(name, cfg.DefaultNameMapper, cfg.DefaultIdMapper); + } + + /** <inheritDoc /> */ + public Type Type + { + get { return null; } + } + + /** <inheritDoc /> */ + public int TypeId + { + get { return _id; } + } + + /** <inheritDoc /> */ + public string TypeName + { + get { return _name; } + } + + /** <inheritDoc /> */ + public bool UserType + { + get { return true; } + } + + /** <inheritDoc /> */ + public bool MetadataEnabled + { + get { return _cfg.DefaultMetadataEnabled; } + } + + /** <inheritDoc /> */ + public bool KeepDeserialized + { + get { return _cfg.DefaultKeepDeserialized; } + } + + /** <inheritDoc /> */ + public IPortableNameMapper NameConverter + { + get { return _cfg.DefaultNameMapper; } + } + + /** <inheritDoc /> */ + public IPortableIdMapper Mapper + { + get { return _cfg.DefaultIdMapper; } + } + + /** <inheritDoc /> */ + public IPortableSerializer Serializer + { + get { return _cfg.DefaultSerializer; } + } + + /** <inheritDoc /> */ + public string AffinityKeyFieldName + { + get { return null; } + } + + /** <inheritDoc /> */ + public object TypedHandler + { + get { return null; } + } + + /** <inheritDoc /> */ + public PortableSystemWriteDelegate UntypedHandler + { + get { return null; } + } + } +}
