/// <summary> /// Initializes an ActionScript source mapping descriptor. /// </summary> /// <param name="sourceKind">The source value kind</param> /// <param name="sourceClassAlias">The source class alias, may be empty but never null</param> /// <param name="targetNativeType">The type of native objects to which values are mapped, never null</param> /// <param name="sourceContentFlags">Flags that describe abstract properties of the contents of the source value</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="targetNativeType"/> or /// <see cref="sourceClassAlias" /> is null</exception> public ASSourceMappingDescriptor(ASTypeKind sourceKind, string sourceClassAlias, ASValueContentFlags sourceContentFlags, Type targetNativeType) { if (targetNativeType == null) { throw new ArgumentNullException("sourceNativeType"); } if (sourceClassAlias == null) { throw new ArgumentNullException("sourceClassAlias"); } this.sourceKind = sourceKind; this.sourceClassAlias = sourceClassAlias; this.sourceContentFlags = sourceContentFlags; this.targetNativeType = targetNativeType; }
/// <summary> /// Gets the default native type to use for mapping ActionScript values with certain properties. /// </summary> /// <remarks> /// The default native type can be used when insufficient type information is available to /// perform a mapping unambiguously. /// </remarks> /// <param name="kind">The value's kind</param> /// <param name="classAlias">The value's class alias or an empty string if none</param> /// <param name="contentFlags">The value's content flags</param> /// <returns>The default native type</returns> public Type GetDefaultNativeType(ASTypeKind kind, string classAlias, ASValueContentFlags contentFlags) { switch (kind) { case ASTypeKind.Array: if ((contentFlags & ASValueContentFlags.HasDynamicProperties) == 0) return typeof(object[]); if ((contentFlags & ASValueContentFlags.HasIndexedValues) == 0) return typeof(Dictionary<string, object>); return typeof(MixedArray<object>); case ASTypeKind.Boolean: return typeof(bool); case ASTypeKind.ByteArray: return typeof(byte[]); case ASTypeKind.Date: return typeof(DateTime); case ASTypeKind.Int29: return typeof(int); case ASTypeKind.Null: return typeof(object); case ASTypeKind.Number: return typeof(double); case ASTypeKind.Object: if (classAlias.Length != 0) { ActionScriptClassMapping classMapping = GetClassMappingByAlias(classAlias); if (classMapping != null) return classMapping.NativeType; } // Map untyped and unknown objects to dictionaries. return typeof(Dictionary<string, object>); case ASTypeKind.String: return typeof(string); case ASTypeKind.Undefined: return typeof(ASUndefined); case ASTypeKind.Unsupported: return typeof(ASUnsupported); case ASTypeKind.Xml: return typeof(XmlDocument); default: throw new ActionScriptException(String.Format(CultureInfo.CurrentCulture, "Unsupported type kind '{0}'.", kind)); } }
private object UncachedToNative(IASValue asValue, Type nativeType) { ASTypeKind kind = asValue.Kind; ASClass asClass = asValue.Class; string classAlias = asClass != null ? asClass.ClassAlias : ""; ASValueContentFlags contentFlags = asValue.ContentFlags; Type defaultNativeType = mappingTable.GetDefaultNativeType(kind, classAlias, contentFlags); // If the requested native type is not as precise as the default native type, // then use the default native type instead. This rule is intended to avoid // the ambiguities that occur if we try to map to type "object" or some other // type that is too general. if (nativeType == null || nativeType.IsAssignableFrom(defaultNativeType)) { nativeType = defaultNativeType; } // Quick short circuit if the desired type is a subtype of IASValue so mapping won't help. if (!typeof(IASValue).IsAssignableFrom(nativeType)) { // Note: This will handle uninitialized values by returning null if no trivial // conversion is possible without complete initialization. object value = asValue.GetNativeValue(nativeType); if (value != null) { return(value); } // If the value isn't initialized then give up because we might have to do mapping. if (!asValue.IsInitialized) { throw new ActionScriptException(String.Format(CultureInfo.CurrentCulture, "The ActionScript value cannot be mapped to type '{0}' because it is not completely initialized.", nativeType != null ? nativeType.FullName : "<default>")); } // Use mappers. // FIXME: This won't work if mapping ends up being recursive! ASSourceMappingDescriptor descriptor = new ASSourceMappingDescriptor(kind, classAlias, contentFlags, nativeType); IASSourceMapper mapper = mappingTable.GetASSourceMapper(descriptor); if (mapper != null) { return(mapper.ToNative(this, asValue, nativeType)); } } // Apply default handling for null references. if (asValue == ASNull.Value && !nativeType.IsValueType) { return(null); } // As a last resort, if we can assign the AS value to the original type requested then // do so. We generally prefer mappings over returning IASValue instances, but if there // is no other choice... if (nativeType.IsInstanceOfType(asValue)) { return(asValue); } // Give up! throw new ActionScriptException(String.Format(CultureInfo.CurrentCulture, "Cannot find a suitable mapper for mapping an ActionScript value of kind '{0}' with class alias '{1}' to an instance of type '{2}'.", asValue.Kind, classAlias, nativeType != null ? nativeType.FullName : "<default>")); }
/// <summary> /// Gets the default native type to use for mapping ActionScript values with certain properties. /// </summary> /// <remarks> /// The default native type can be used when insufficient type information is available to /// perform a mapping unambiguously. /// </remarks> /// <param name="kind">The value's kind</param> /// <param name="classAlias">The value's class alias or an empty string if none</param> /// <param name="contentFlags">The value's content flags</param> /// <returns>The default native type</returns> public Type GetDefaultNativeType(ASTypeKind kind, string classAlias, ASValueContentFlags contentFlags) { switch (kind) { case ASTypeKind.Array: if ((contentFlags & ASValueContentFlags.HasDynamicProperties) == 0) { return(typeof(object[])); } if ((contentFlags & ASValueContentFlags.HasIndexedValues) == 0) { return(typeof(Dictionary <string, object>)); } return(typeof(MixedArray <object>)); case ASTypeKind.Boolean: return(typeof(bool)); case ASTypeKind.ByteArray: return(typeof(byte[])); case ASTypeKind.Date: return(typeof(DateTime)); case ASTypeKind.Int29: return(typeof(int)); case ASTypeKind.Null: return(typeof(object)); case ASTypeKind.Number: return(typeof(double)); case ASTypeKind.Object: if (classAlias.Length != 0) { ActionScriptClassMapping classMapping = GetClassMappingByAlias(classAlias); if (classMapping != null) { return(classMapping.NativeType); } } // Map untyped and unknown objects to dictionaries. return(typeof(Dictionary <string, object>)); case ASTypeKind.String: return(typeof(string)); case ASTypeKind.Undefined: return(typeof(ASUndefined)); case ASTypeKind.Unsupported: return(typeof(ASUnsupported)); case ASTypeKind.Xml: return(typeof(XmlDocument)); default: throw new ActionScriptException(String.Format(CultureInfo.CurrentCulture, "Unsupported type kind '{0}'.", kind)); } }