/// <summary> /// Writes an implementation of the <see cref="IDataSourceWrapper"/> interface. /// </summary> /// <param name="dataSourceWrapperInfo">A <see cref="DataSourceWrapperInfo"/> describing the data source wrapper for which to write an <see cref="IDataSourceWrapper"/> implementation.</param> public void WriteIDataSourceWrapperImplementation(DataSourceWrapperInfo dataSourceWrapperInfo) { WriteLine("public override Object WrappedDataSource"); WriteLine("{"); WriteLine("get { return value; }"); WriteLine("}"); WriteLine("private readonly {0} value;", CSharpLanguage.GetCSharpTypeName(dataSourceWrapperInfo.DataSourceType)); }
/// <summary> /// Writes a constructor for the specified data source wrapper. /// </summary> /// <param name="dataSourceWrapperInfo">A <see cref="DataSourceWrapperInfo"/> describing the data source for which to write a constructor.</param> public void WriteConstructor(DataSourceWrapperInfo dataSourceWrapperInfo) { WriteLine("public {0}({1} dataSource, {2} namescope) : base(namescope)", dataSourceWrapperInfo.DataSourceWrapperName, CSharpLanguage.GetCSharpTypeName(dataSourceWrapperInfo.DataSourceType), CSharpLanguage.GetCSharpTypeName(typeof(Namescope))); WriteLine("{"); WriteLine("this.value = dataSource;"); WriteLine("}"); }
/// <summary> /// Given a targeted binding expression (in the form "foo->bar"), this method extracts the target name, target type, and expression text. /// </summary> private Boolean GetExpressionTargetInfo(IExpressionCompilerState state, DataSourceWrapperInfo dataSourceWrapperInfo, XObject source, ref String expText, out String expTarget, out Type expTargetType) { const string TargetExpressionDelimiter = "->"; var expOriginal = expText; var delimiterIndex = expText.IndexOf(TargetExpressionDelimiter); if (delimiterIndex >= 0) { var expPartTarget = expText.Substring(0, delimiterIndex); var expPartText = expText.Substring(delimiterIndex + TargetExpressionDelimiter.Length); var matchCandidates = (from element in dataSourceWrapperInfo.DataSourceDefinition.Definition.Descendants() where (String)element.Attribute("Name") == expPartTarget select element).ToList(); if (matchCandidates.Count == 0) { throw new BindingExpressionCompilationErrorException(source, dataSourceWrapperInfo.DataSourceDefinition.DefinitionPath, CompilerStrings.ExpressionTargetIsNotFound.Format(expPartTarget)); } if (matchCandidates.Count > 1) { throw new BindingExpressionCompilationErrorException(source, dataSourceWrapperInfo.DataSourceDefinition.DefinitionPath, CompilerStrings.ExpressionTargetIsAmbiguous.Format(expPartTarget)); } var match = matchCandidates.Single(); var matchName = match.Name.LocalName; expText = expPartText; expTargetType = UvmlTypeAnalysis.GetPlaceholderType(dataSourceWrapperInfo.DataSourceType, matchName); if (expTargetType == null && !state.GetKnownType(matchName, out expTargetType)) { throw new BindingExpressionCompilationErrorException(source, dataSourceWrapperInfo.DataSourceDefinition.DefinitionPath, CompilerStrings.ExpressionTargetIsUnrecognizedType.Format(expOriginal, matchName)); } expTarget = String.Format("__UPF_FindName<{0}>(\"{1}\").", CSharpLanguage.GetCSharpTypeName(expTargetType), expPartTarget); return(true); } expTarget = default(String); expTargetType = dataSourceWrapperInfo.DataSourceType; return(false); }
/// <summary> /// Writes the source code for the specified data source wrapper. /// </summary> private static void WriteSourceCodeForDataSourceWrapper(RoslynExpressionCompilerState state, DataSourceWrapperInfo dataSourceWrapperInfo) { using (var writer = new DataSourceWrapperWriter()) { // Using statements var imports = Enumerable.Union(new[] { "System" }, dataSourceWrapperInfo.Imports).Distinct().OrderBy(x => x); foreach (var import in imports) { writer.WriteLine("using {0};", import); } writer.WriteLine(); // Namespace declaration var @namespace = dataSourceWrapperInfo.DataSourceDefinition.DataSourceWrapperNamespace; writer.WriteLine("namespace " + @namespace); writer.WriteLine("{"); writer.WriteLine("#pragma warning disable 1591"); writer.WriteLine("#pragma warning disable 0184"); // Data source wrapper class - main WriteSourceCodeForDataSourceWrapperClass(state, dataSourceWrapperInfo, writer); // Data source wrapper class - dependent foreach (var dependentWrapperInfo in dataSourceWrapperInfo.DependentWrapperInfos) { WriteSourceCodeForDataSourceWrapperClass(state, dependentWrapperInfo, writer); } // Namespace complete writer.WriteLine("#pragma warning restore 0184"); writer.WriteLine("#pragma warning restore 1591"); writer.WriteLine("}"); dataSourceWrapperInfo.DataSourceWrapperSourceCode = writer.ToString(); } }
/// <summary> /// Writes the source code for an individual data source wrapper class. /// </summary> private static void WriteSourceCodeForDataSourceWrapperClass(RoslynExpressionCompilerState state, DataSourceWrapperInfo dataSourceWrapperInfo, DataSourceWrapperWriter writer) { // Class declaration writer.WriteLine("// Generated by the UPF Binding Expression Compiler, version {0}", FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion); writer.WriteLine("[System.CLSCompliant(false)]"); writer.WriteLine("[Ultraviolet.Presentation.WrappedDataSource(typeof({0}))]", CSharpLanguage.GetCSharpTypeName(dataSourceWrapperInfo.DataSourceType)); writer.WriteLine("public sealed partial class {0} : {1}", dataSourceWrapperInfo.DataSourceWrapperName, CSharpLanguage.GetCSharpTypeName(typeof(CompiledDataSourceWrapper))); writer.WriteLine("{"); // Constructors writer.WriteLine("#region Constructors"); writer.WriteConstructor(dataSourceWrapperInfo); writer.WriteLine("#endregion"); writer.WriteLine(); // IDataSourceWrapper writer.WriteLine("#region IDataSourceWrapper"); writer.WriteIDataSourceWrapperImplementation(dataSourceWrapperInfo); writer.WriteLine("#endregion"); writer.WriteLine(); // Methods writer.WriteLine("#region Methods"); var methods = dataSourceWrapperInfo.DataSourceType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); foreach (var method in methods) { if (!ExpressionUtil.NeedsWrapper(method)) { continue; } writer.WriteWrapperMethod(method); } writer.WriteLine("#endregion"); writer.WriteLine(); // Properties writer.WriteLine("#region Properties"); var properties = dataSourceWrapperInfo.DataSourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); foreach (var property in properties) { if (!ExpressionUtil.NeedsWrapper(property)) { continue; } writer.WriteWrapperProperty(property); } writer.WriteLine("#endregion"); writer.WriteLine(); // Fields writer.WriteLine("#region Fields"); var fields = dataSourceWrapperInfo.DataSourceType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); foreach (var field in fields) { if (!ExpressionUtil.NeedsWrapper(field)) { continue; } writer.WriteWrapperProperty(field); } writer.WriteLine("#endregion"); writer.WriteLine(); // Expressions writer.WriteLine("#region Expressions"); for (int i = 0; i < dataSourceWrapperInfo.Expressions.Count; i++) { var expressionInfo = dataSourceWrapperInfo.Expressions[i]; writer.WriteExpressionProperty(state, dataSourceWrapperInfo, expressionInfo, i); } writer.WriteLine("#endregion"); writer.WriteLine(); // Special-case binding delegates writer.WriteLine("#region Binding Delegates"); if (typeof(Controls.ContentControl).IsAssignableFrom(dataSourceWrapperInfo.DataSourceType)) { // ContentControl writer.WriteLine("public static readonly DataBindingGetter<System.Object> __GetContent = " + "new DataBindingGetter<System.Object>(vm => (({0})vm).Content);", dataSourceWrapperInfo.DataSourceWrapperName); writer.WriteLine("public static readonly DataBindingGetter<System.String> __GetContentStringFormat = " + "new DataBindingGetter<System.String>(vm => (({0})vm).ContentStringFormat);", dataSourceWrapperInfo.DataSourceWrapperName); writer.WriteLine("public static readonly DataBindingSetter<System.Object> __SetContent = " + "new DataBindingSetter<System.Object>((vm, value) => (({0})vm).Content = value);", dataSourceWrapperInfo.DataSourceWrapperName); writer.WriteLine("public static readonly DataBindingSetter<System.String> __SetContentStringFormat = " + "new DataBindingSetter<System.String>((vm, value) => (({0})vm).ContentStringFormat = value);", dataSourceWrapperInfo.DataSourceWrapperName); } writer.WriteLine("#endregion"); // Class complete writer.WriteLine("}"); }
/// <summary> /// Writes a property which wraps a binding expression. /// </summary> /// <param name="state">The expression compiler's current state.</param> /// <param name="dataSourceWrapperInfo">A <see cref="DataSourceWrapperInfo"/> describing the data source for which to write an expression property.</param> /// <param name="expressionInfo">The binding expression for which to write a property.</param> /// <param name="id">The expression's identifier within the view model.</param> public void WriteExpressionProperty(IExpressionCompilerState state, DataSourceWrapperInfo dataSourceWrapperInfo, BindingExpressionInfo expressionInfo, Int32 id) { var isDependencyProperty = false; var isSimpleDependencyProperty = false; var expText = BindingExpressions.GetBindingMemberPathPart(expressionInfo.Expression); var expTarget = default(String); var expTargetType = dataSourceWrapperInfo.DataSourceType; var expFormatString = BindingExpressions.GetBindingFormatStringPart(expressionInfo.Expression); var targeted = GetExpressionTargetInfo(state, dataSourceWrapperInfo, expressionInfo.Source, ref expText, out expTarget, out expTargetType); var dprop = DependencyProperty.FindByName(expText, expTargetType); var dpropField = default(FieldInfo); if (dprop != null) { isDependencyProperty = true; dpropField = (from prop in dprop.OwnerType.GetFields(BindingFlags.Public | BindingFlags.Static) where prop.FieldType == typeof(DependencyProperty) && prop.GetValue(null) == dprop select prop).SingleOrDefault(); if (dpropField == null) { throw new BindingExpressionCompilationErrorException(expressionInfo.Source, dataSourceWrapperInfo.DataSourceDefinition.DefinitionPath, PresentationStrings.CannotFindDependencyPropertyField.Format(dprop.OwnerType.Name, dprop.Name)); } if (String.IsNullOrEmpty(expFormatString) && !targeted) { isSimpleDependencyProperty = true; } } WriteLine("[{0}(@\"{1}\", SimpleDependencyPropertyOwner = {2}, SimpleDependencyPropertyName = {3})]", typeof(CompiledBindingExpressionAttribute).FullName, expressionInfo.Expression.Replace("\"", "\"\""), isSimpleDependencyProperty ? "typeof(" + CSharpLanguage.GetCSharpTypeName(dprop.OwnerType) + ")" : "null", isSimpleDependencyProperty ? "\"" + dprop.Name + "\"" : "null"); WriteLine("public {0} __UPF_Expression{1}", CSharpLanguage.GetCSharpTypeName(expressionInfo.Type), id); WriteLine("{"); if (expressionInfo.GenerateGetter) { expressionInfo.GetterLineStart = LineCount; var getexp = default(String); if (isDependencyProperty) { getexp = String.Format("{0}GetValue<{1}>({2}.{3})", expTarget, CSharpLanguage.GetCSharpTypeName(dprop.PropertyType), CSharpLanguage.GetCSharpTypeName(dprop.OwnerType), dpropField.Name); } else { getexp = String.Format("{0}{1}", expTarget, expText); } var hasFormatString = !String.IsNullOrEmpty(expFormatString); expFormatString = hasFormatString ? String.Format("\"{{0:{0}}}\"", expFormatString) : "null"; if (IsStringType(expressionInfo.Type) || (expressionInfo.Type == typeof(Object) && hasFormatString)) { WriteLine("get"); WriteLine("{"); WriteLine("var value = {0};", getexp); WriteLine("return ({0})__UPF_ConvertToString(value, {1});", CSharpLanguage.GetCSharpTypeName(expressionInfo.Type), expFormatString); WriteLine("}"); } else { WriteLine("get {{ return ({0})({1}); }}", CSharpLanguage.GetCSharpTypeName(expressionInfo.Type), getexp); } expressionInfo.GetterLineEnd = LineCount - 1; } if (dprop != null && dprop.IsReadOnly) { expressionInfo.GenerateSetter = false; } if (expressionInfo.GenerateSetter) { var targetTypeName = expressionInfo.CS0266TargetType; var targetTypeSpecified = !String.IsNullOrEmpty(targetTypeName); expressionInfo.SetterLineStart = LineCount; if (isDependencyProperty) { if (IsStringType(expressionInfo.Type)) { WriteLine("set"); WriteLine("{"); WriteLine("var current = {0}GetValue<{1}>({2}.{3});", expTarget, CSharpLanguage.GetCSharpTypeName(dprop.PropertyType), CSharpLanguage.GetCSharpTypeName(dprop.OwnerType), dpropField.Name); WriteLine("{0}SetValue<{1}>({2}.{3}, __UPF_ConvertFromString(value, current));", expTarget, CSharpLanguage.GetCSharpTypeName(dprop.PropertyType), CSharpLanguage.GetCSharpTypeName(dprop.OwnerType), dpropField.Name); WriteLine("}"); } else { if (expressionInfo.NullableFixup) { WriteLine(targetTypeSpecified ? "set {{ {0}SetValue<{1}>({2}.{3}, ({4})(value ?? default({1}))); }}" : "set {{ {0}SetValue<{1}>({2}.{3}, value ?? default({1})); }}", expTarget, CSharpLanguage.GetCSharpTypeName(dprop.PropertyType), CSharpLanguage.GetCSharpTypeName(dprop.OwnerType), dpropField.Name, targetTypeName); } else { WriteLine(targetTypeSpecified ? "set {{ {0}SetValue<{1}>({2}.{3}, ({4})(value)); }}" : "set {{ {0}SetValue<{1}>({2}.{3}, value); }}", expTarget, CSharpLanguage.GetCSharpTypeName(dprop.PropertyType), CSharpLanguage.GetCSharpTypeName(dprop.OwnerType), dpropField.Name, targetTypeName); } } } else { if (IsStringType(expressionInfo.Type)) { WriteLine("set"); WriteLine("{"); WriteLine("var current = {0}{1};", expTarget, expText); WriteLine("{0}{1} = __UPF_ConvertFromString(value, current);", expTarget, expText); WriteLine("}"); } else { if (expressionInfo.NullableFixup) { WriteLine(targetTypeSpecified ? "set {{ ({0}{1}) = ({3})(value ?? default({2})); }}" : "set {{ ({0}{1}) = value ?? default({2}); }}", expTarget, expText, CSharpLanguage.GetCSharpTypeName(Nullable.GetUnderlyingType(expressionInfo.Type)), targetTypeName); } else { WriteLine(targetTypeSpecified ? "set {{ ({0}{1}) = ({2})(value); }}" : "set {{ ({0}{1}) = value; }}", expTarget, expText, targetTypeName); } } } expressionInfo.SetterLineEnd = LineCount - 1; } WriteLine("}"); WriteLine(); if (expressionInfo.GenerateGetter) { WriteLine("public static readonly DataBindingGetter<{0}> __Get__UPF_Expression{1} = new DataBindingGetter<{0}>(vm => (({2})vm).__UPF_Expression{1});", CSharpLanguage.GetCSharpTypeName(expressionInfo.Type), id, dataSourceWrapperInfo.DataSourceWrapperName); WriteLine(); } if (expressionInfo.GenerateSetter) { WriteLine("public static readonly DataBindingSetter<{0}> __Set__UPF_Expression{1} = new DataBindingSetter<{0}>((vm, value) => (({2})vm).__UPF_Expression{1} = value);", CSharpLanguage.GetCSharpTypeName(expressionInfo.Type), id, dataSourceWrapperInfo.DataSourceWrapperName); WriteLine(); } }
/// <summary> /// Gets the name of the file in which the specified data source wrapper's source code is saved during compilation. /// </summary> /// <param name="dataSourceWrapperInfo">The <see cref="DataSourceWrapperInfo"/> for which to retrieve a file name.</param> /// <returns>The name of the file in which the specified data souurce wrapper's source code is saved during compilation.</returns> public String GetWorkingFileForDataSourceWrapper(DataSourceWrapperInfo dataSourceWrapperInfo) { return(Path.ChangeExtension(Path.Combine(Path.GetTempPath(), dataSourceWrapperInfo.UniqueID.ToString()), "cs")); }