1 //------------------------------------------------------------------------------
2 // <copyright file="Models.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml.Serialization {
11 using System.Reflection;
12 using System.Collections;
13 using System.Diagnostics;
15 // These classes define the abstract serialization model, e.g. the rules for WHAT is serialized.
16 // The answer of HOW the values are serialized is answered by a particular reflection importer
17 // by looking for a particular set of custom attributes specific to the serialization format
18 // and building an appropriate set of accessors/mappings.
20 internal class ModelScope {
22 Hashtable models = new Hashtable();
23 Hashtable arrayModels = new Hashtable();
25 internal ModelScope(TypeScope typeScope) {
26 this.typeScope = typeScope;
29 internal TypeScope TypeScope {
30 get { return typeScope; }
33 internal TypeModel GetTypeModel(Type type) {
34 return GetTypeModel(type, true);
37 internal TypeModel GetTypeModel(Type type, bool directReference) {
38 TypeModel model = (TypeModel)models[type];
39 if (model != null) return model;
40 TypeDesc typeDesc = typeScope.GetTypeDesc(type, null, directReference);
42 switch (typeDesc.Kind) {
44 model = new EnumModel(type, typeDesc, this);
46 case TypeKind.Primitive:
47 model = new PrimitiveModel(type, typeDesc, this);
50 case TypeKind.Collection:
51 case TypeKind.Enumerable:
52 model = new ArrayModel(type, typeDesc, this);
57 model = new StructModel(type, typeDesc, this);
60 if (!typeDesc.IsSpecial) throw new NotSupportedException(Res.GetString(Res.XmlUnsupportedTypeKind, type.FullName));
61 model = new SpecialModel(type, typeDesc, this);
65 models.Add(type, model);
69 internal ArrayModel GetArrayModel(Type type) {
70 TypeModel model = (TypeModel)arrayModels[type];
72 model = GetTypeModel(type);
73 if (!(model is ArrayModel)) {
74 TypeDesc typeDesc = typeScope.GetArrayTypeDesc(type);
75 model = new ArrayModel(type, typeDesc, this);
77 arrayModels.Add(type, model);
79 return (ArrayModel)model;
83 internal abstract class TypeModel {
88 protected TypeModel(Type type, TypeDesc typeDesc, ModelScope scope) {
91 this.typeDesc = typeDesc;
98 internal ModelScope ModelScope {
102 internal TypeDesc TypeDesc {
103 get { return typeDesc; }
107 internal class ArrayModel : TypeModel {
108 internal ArrayModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { }
110 internal TypeModel Element {
111 get { return ModelScope.GetTypeModel(TypeScope.GetArrayElementType(Type, null)); }
115 internal class PrimitiveModel : TypeModel {
116 internal PrimitiveModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { }
119 internal class SpecialModel : TypeModel {
120 internal SpecialModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { }
123 internal class StructModel : TypeModel {
125 internal StructModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { }
127 internal MemberInfo[] GetMemberInfos() {
128 // we use to return Type.GetMembers() here, the members were returned in a different order: fields first, properties last
129 // Current System.Reflection code returns members in oposite order: properties first, then fields.
130 // This code make sure that returns members in the Everett order.
131 MemberInfo[] members = Type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
132 MemberInfo[] fieldsAndProps = new MemberInfo[members.Length];
135 // first copy all non-property members over
136 for (int i = 0; i < members.Length; i++) {
137 if ((members[i].MemberType & MemberTypes.Property) == 0) {
138 fieldsAndProps[cMember++] = members[i];
141 // now copy all property members over
142 for (int i = 0; i < members.Length; i++) {
143 if ((members[i].MemberType & MemberTypes.Property) != 0) {
144 fieldsAndProps[cMember++] = members[i];
147 return fieldsAndProps;
150 internal FieldModel GetFieldModel(MemberInfo memberInfo) {
151 FieldModel model = null;
152 if (memberInfo is FieldInfo)
153 model = GetFieldModel((FieldInfo)memberInfo);
154 else if (memberInfo is PropertyInfo)
155 model = GetPropertyModel((PropertyInfo)memberInfo);
157 if (model.ReadOnly && model.FieldTypeDesc.Kind != TypeKind.Collection && model.FieldTypeDesc.Kind != TypeKind.Enumerable)
163 void CheckSupportedMember(TypeDesc typeDesc, MemberInfo member, Type type) {
164 if (typeDesc == null)
166 if (typeDesc.IsUnsupported) {
167 if (typeDesc.Exception == null) {
168 typeDesc.Exception = new NotSupportedException(Res.GetString(Res.XmlSerializerUnsupportedType, typeDesc.FullName));
170 throw new InvalidOperationException(Res.GetString(Res.XmlSerializerUnsupportedMember, member.DeclaringType.FullName + "." + member.Name, type.FullName), typeDesc.Exception);
173 CheckSupportedMember(typeDesc.BaseTypeDesc, member, type);
174 CheckSupportedMember(typeDesc.ArrayElementTypeDesc, member, type);
177 FieldModel GetFieldModel(FieldInfo fieldInfo) {
178 if (fieldInfo.IsStatic) return null;
179 if (fieldInfo.DeclaringType != Type) return null;
181 TypeDesc typeDesc = ModelScope.TypeScope.GetTypeDesc(fieldInfo.FieldType, fieldInfo, true, false);
182 if (fieldInfo.IsInitOnly && typeDesc.Kind != TypeKind.Collection && typeDesc.Kind != TypeKind.Enumerable)
185 CheckSupportedMember(typeDesc, fieldInfo, fieldInfo.FieldType);
186 return new FieldModel(fieldInfo, fieldInfo.FieldType, typeDesc);
189 FieldModel GetPropertyModel(PropertyInfo propertyInfo) {
190 if (propertyInfo.DeclaringType != Type) return null;
191 if (CheckPropertyRead(propertyInfo)) {
192 TypeDesc typeDesc = ModelScope.TypeScope.GetTypeDesc(propertyInfo.PropertyType, propertyInfo, true, false);
193 // Fix for CSDMain 100492, please contact arssrvlt if you need to change this line
194 if (!propertyInfo.CanWrite && typeDesc.Kind != TypeKind.Collection && typeDesc.Kind != TypeKind.Enumerable)
196 CheckSupportedMember(typeDesc, propertyInfo, propertyInfo.PropertyType);
197 return new FieldModel(propertyInfo, propertyInfo.PropertyType, typeDesc);
203 internal static bool CheckPropertyRead(PropertyInfo propertyInfo) {
204 if (!propertyInfo.CanRead) return false;
206 MethodInfo getMethod = propertyInfo.GetGetMethod();
207 if (getMethod.IsStatic) return false;
208 ParameterInfo[] parameters = getMethod.GetParameters();
209 if (parameters.Length > 0) return false;
214 internal enum SpecifiedAccessor {
220 internal class FieldModel {
221 SpecifiedAccessor checkSpecified = SpecifiedAccessor.None;
222 MemberInfo memberInfo;
223 MemberInfo checkSpecifiedMemberInfo;
224 MethodInfo checkShouldPersistMethodInfo;
225 bool checkShouldPersist;
226 bool readOnly = false;
227 bool isProperty = false;
230 TypeDesc fieldTypeDesc;
232 internal FieldModel(string name, Type fieldType, TypeDesc fieldTypeDesc, bool checkSpecified, bool checkShouldPersist) :
233 this(name, fieldType, fieldTypeDesc, checkSpecified, checkShouldPersist, false) {
235 internal FieldModel(string name, Type fieldType, TypeDesc fieldTypeDesc, bool checkSpecified, bool checkShouldPersist, bool readOnly) {
236 this.fieldTypeDesc = fieldTypeDesc;
238 this.fieldType = fieldType;
239 this.checkSpecified = checkSpecified ? SpecifiedAccessor.ReadWrite : SpecifiedAccessor.None;
240 this.checkShouldPersist = checkShouldPersist;
241 this.readOnly = readOnly;
244 internal FieldModel(MemberInfo memberInfo, Type fieldType, TypeDesc fieldTypeDesc) {
245 this.name = memberInfo.Name;
246 this.fieldType = fieldType;
247 this.fieldTypeDesc = fieldTypeDesc;
248 this.memberInfo = memberInfo;
249 this.checkShouldPersistMethodInfo = memberInfo.DeclaringType.GetMethod("ShouldSerialize" + memberInfo.Name, new Type[0]);
250 this.checkShouldPersist = this.checkShouldPersistMethodInfo != null;
252 FieldInfo specifiedField = memberInfo.DeclaringType.GetField(memberInfo.Name + "Specified");
253 if (specifiedField != null) {
254 if (specifiedField.FieldType != typeof(bool)) {
255 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidSpecifiedType, specifiedField.Name, specifiedField.FieldType.FullName, typeof(bool).FullName));
257 this.checkSpecified = specifiedField.IsInitOnly ? SpecifiedAccessor.ReadOnly : SpecifiedAccessor.ReadWrite;
258 this.checkSpecifiedMemberInfo = specifiedField;
261 PropertyInfo specifiedProperty = memberInfo.DeclaringType.GetProperty(memberInfo.Name + "Specified");
262 if (specifiedProperty != null) {
263 if (StructModel.CheckPropertyRead(specifiedProperty)) {
264 this.checkSpecified = specifiedProperty.CanWrite ? SpecifiedAccessor.ReadWrite : SpecifiedAccessor.ReadOnly;
265 this.checkSpecifiedMemberInfo = specifiedProperty;
267 if (this.checkSpecified != SpecifiedAccessor.None && specifiedProperty.PropertyType != typeof(bool)) {
268 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidSpecifiedType, specifiedProperty.Name, specifiedProperty.PropertyType.FullName, typeof(bool).FullName));
272 if (memberInfo is PropertyInfo) {
273 readOnly = !((PropertyInfo)memberInfo).CanWrite;
276 else if (memberInfo is FieldInfo) {
277 readOnly = ((FieldInfo)memberInfo).IsInitOnly;
281 internal string Name {
285 internal Type FieldType {
286 get { return fieldType; }
289 internal TypeDesc FieldTypeDesc {
290 get { return fieldTypeDesc; }
293 internal bool CheckShouldPersist {
294 get { return checkShouldPersist; }
297 internal SpecifiedAccessor CheckSpecified {
298 get { return checkSpecified; }
301 internal MemberInfo MemberInfo {
302 get { return memberInfo; }
304 internal MemberInfo CheckSpecifiedMemberInfo {
305 get { return checkSpecifiedMemberInfo; }
307 internal MethodInfo CheckShouldPersistMethodInfo {
308 get { return checkShouldPersistMethodInfo; }
311 internal bool ReadOnly {
312 get { return readOnly; }
315 internal bool IsProperty {
316 get { return isProperty; }
320 internal class ConstantModel {
324 internal ConstantModel(FieldInfo fieldInfo, long value) {
325 this.fieldInfo = fieldInfo;
329 internal string Name {
330 get { return fieldInfo.Name; }
333 internal long Value {
334 get { return value; }
337 internal FieldInfo FieldInfo {
338 get { return fieldInfo; }
342 internal class EnumModel : TypeModel {
343 ConstantModel[] constants;
345 internal EnumModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { }
347 internal ConstantModel[] Constants {
349 if (constants == null) {
350 ArrayList list = new ArrayList();
351 FieldInfo[] fields = Type.GetFields();
352 for (int i = 0; i < fields.Length; i++) {
353 FieldInfo field = fields[i];
354 ConstantModel constant = GetConstantModel(field);
355 if (constant != null) list.Add(constant);
357 constants = (ConstantModel[])list.ToArray(typeof(ConstantModel));
364 ConstantModel GetConstantModel(FieldInfo fieldInfo) {
365 if (fieldInfo.IsSpecialName) return null;
366 return new ConstantModel(fieldInfo, ((IConvertible)fieldInfo.GetValue(null)).ToInt64(null));