private object ReadArray(string typeName, string typeNs)
{
SoapArrayInfo arrayInfo;
Type fallbackElementType = null;
if (_soap12)
{
string itemType = _r.GetAttribute(_itemTypeID, _soap12NsID);
string arraySize = _r.GetAttribute(_arraySizeID, _soap12NsID);
Type arrayType = (Type)_types[new XmlQualifiedName(typeName, typeNs)];
// no indication that this is an array?
if (itemType == null && arraySize == null && (arrayType == null || !arrayType.IsArray))
return null;
arrayInfo = ParseSoap12ArrayType(itemType, arraySize);
if (arrayType != null)
fallbackElementType = TypeScope.GetArrayElementType(arrayType, null);
}
else
{
string arrayType = _r.GetAttribute(_arrayTypeID, _soapNsID);
if (arrayType == null)
return null;
arrayInfo = ParseArrayType(arrayType);
}
if (arrayInfo.dimensions != 1) throw new InvalidOperationException(SR.Format(SR.XmlInvalidArrayDimentions, CurrentTag()));
// NOTE: don't use the array size that is specified since an evil client might pass
// a number larger than the actual number of items in an attempt to harm the server.
XmlQualifiedName qname;
bool isPrimitive;
Type elementType = null;
XmlQualifiedName urTypeName = new XmlQualifiedName(_urTypeID, _schemaNsID);
if (arrayInfo.qname.Length > 0)
{
qname = ToXmlQualifiedName(arrayInfo.qname, false);
elementType = (Type)_types[qname];
}
else
qname = urTypeName;
// try again if the best we could come up with was object
if (_soap12 && elementType == typeof(object))
elementType = null;
if (elementType == null)
{
if (!_soap12)
{
elementType = GetPrimitiveType(qname, true);
isPrimitive = true;
}
else
{
// try it as a primitive
if (qname != urTypeName)
elementType = GetPrimitiveType(qname, false);
if (elementType != null)
{
isPrimitive = true;
}
else
{
// still nothing: go with fallback type or object
if (fallbackElementType == null)
{
elementType = typeof(object);
isPrimitive = false;
}
else
{
elementType = fallbackElementType;
XmlQualifiedName newQname = (XmlQualifiedName)_typesReverse[elementType];
if (newQname == null)
{
newQname = XmlSerializationWriter.GetPrimitiveTypeNameInternal(elementType);
isPrimitive = true;
}
else
isPrimitive = elementType.GetTypeInfo().IsPrimitive;
if (newQname != null) qname = newQname;
}
}
}
}
else
isPrimitive = elementType.GetTypeInfo().IsPrimitive;
if (!_soap12 && arrayInfo.jaggedDimensions > 0)
{
for (int i = 0; i < arrayInfo.jaggedDimensions; i++)
elementType = elementType.MakeArrayType();
}
if (_r.IsEmptyElement)
{
_r.Skip();
return Array.CreateInstance(elementType, 0);
}
_r.ReadStartElement();
_r.MoveToContent();
int arrayLength = 0;
Array array = null;
if (elementType.GetTypeInfo().IsValueType)
{
if (!isPrimitive && !elementType.GetTypeInfo().IsEnum)
{
throw new NotSupportedException(SR.Format(SR.XmlRpcArrayOfValueTypes, elementType.FullName));
}
// CONSIDER, erikc, we could have specialized read functions here
// for primitives, which would avoid boxing.
int whileIterations = 0;
int readerCount = ReaderCount;
while (_r.NodeType != XmlNodeType.EndElement)
{
array = EnsureArrayIndex(array, arrayLength, elementType);
array.SetValue(ReadReferencedElement(qname.Name, qname.Namespace), arrayLength);
arrayLength++;
_r.MoveToContent();
CheckReaderCount(ref whileIterations, ref readerCount);
}
array = ShrinkArray(array, arrayLength, elementType, false);
}
else
{
string type;
string typens;
string[] ids = null;
int idsLength = 0;
int whileIterations = 0;
int readerCount = ReaderCount;
while (_r.NodeType != XmlNodeType.EndElement)
{
array = EnsureArrayIndex(array, arrayLength, elementType);
ids = (string[])EnsureArrayIndex(ids, idsLength, typeof(string));
// CONSIDER: i'm not sure it's correct to let item name take precedence over arrayType
if (_r.NamespaceURI.Length != 0)
{
type = _r.LocalName;
if ((object)_r.NamespaceURI == (object)_soapNsID)
typens = XmlSchema.Namespace;
else
typens = _r.NamespaceURI;
}
else
{
type = qname.Name;
typens = qname.Namespace;
}
array.SetValue(ReadReferencingElement(type, typens, out ids[idsLength]), arrayLength);
arrayLength++;
idsLength++;
// CONSIDER, erikc, sparse arrays, perhaps?
_r.MoveToContent();
CheckReaderCount(ref whileIterations, ref readerCount);
}
// special case for soap 1.2: try to get a better fit than object[] when no metadata is known
// this applies in the doc/enc/bare case
if (_soap12 && elementType == typeof(object))
{
Type itemType = null;
for (int i = 0; i < arrayLength; i++)
{
object currItem = array.GetValue(i);
if (currItem != null)
{
Type currItemType = currItem.GetType();
if (currItemType.GetTypeInfo().IsValueType)
{
itemType = null;
break;
}
if (itemType == null || currItemType.IsAssignableFrom(itemType))
{
itemType = currItemType;
}
else if (!itemType.IsAssignableFrom(currItemType))
{
itemType = null;
break;
}
}
}
if (itemType != null)
elementType = itemType;
}
ids = (string[])ShrinkArray(ids, idsLength, typeof(string), false);
array = ShrinkArray(array, arrayLength, elementType, false);
Fixup fixupArray = new Fixup(array, new XmlSerializationFixupCallback(this.FixupArrayRefs), ids);
AddFixup(fixupArray);
}
// CONSIDER, erikc, check to see if the specified array length was right, perhaps?
ReadEndElement();
return array;
}