/// <summary> /// Gets a collection of <see cref="DataSourceWrapperInfo"/> objects for all of the views defined within the specified root directory /// as well as all of the component templates which are currently registered with the Ultraviolet context. /// </summary> /// <param name="state">The expression compiler's current state.</param> /// <param name="root">The root directory to search for views.</param> /// <returns>A collection of <see cref="DataSourceWrapperInfo"/> instances which represent the views and templates which were found.</returns> public static IEnumerable <DataSourceWrapperInfo> GetDataSourceWrapperInfos(IExpressionCompilerState state, String root) { var viewDefinitions = RecursivelySearchForViews(root, root); var viewModelInfos = RetrieveDataSourceWrapperInfos(state, viewDefinitions); var templateDefinitions = RetrieveTemplateDefinitions(state); var templateModelInfos = RetrieveDataSourceWrapperInfos(state, templateDefinitions); return(Enumerable.Union(viewModelInfos, templateModelInfos)); }
/// <summary> /// Attempts to find the dependency or attached property with the specified name. /// </summary> /// <param name="state">The expression compiler's current state.</param> /// <param name="name">The name of the dependency or attached property to retrieve.</param> /// <param name="ownerType">The type that references the dependency or attached property.</param> /// <returns>The <see cref="DependencyProperty"/> referred to by the specified name, or <see langword="null"/> if there is no such dependency property.</returns> private static DependencyProperty FindDependencyOrAttachedPropertyByName(IExpressionCompilerState state, String name, Type ownerType) { if (ExpressionUtil.IsAttachedProperty(name, out var container, out var property)) { if (!state.GetKnownType(container, out var containerType)) return null; return DependencyProperty.FindByName(property, containerType); } return DependencyProperty.FindByName(name, ownerType); }
/// <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> /// Gets a collection of <see cref="DataSourceDefinition"/> instances for any component templates which /// are currently registered with the Ultraviolet Presentation Foundation. /// </summary> /// <returns>A collection of <see cref="DataSourceDefinition"/> instances which represent UPF component template definitions.</returns> private static IEnumerable<DataSourceDefinition> RetrieveTemplateDefinitions(IExpressionCompilerState state) { if (state.ComponentTemplateManager == null) return Enumerable.Empty<DataSourceDefinition>(); var templateDefs = from template in state.ComponentTemplateManager select DataSourceDefinition.FromComponentTemplate(template.Key, template.Value.Root.Element("View")); var templateDefsList = templateDefs.ToList(); foreach (var templateDef in templateDefsList) UvmlLoader.AddUvmlAnnotations(templateDef.DataSourceWrapperName, templateDef.Definition); return templateDefsList; }
/// <summary> /// Installs the specified NuGet package into the current working directory. /// </summary> /// <param name="state">The compiler's current state.</param> /// <param name="packageID">The identifier of the package to install.</param> /// <param name="version">The version of the package to install, or <see langword="null"/> to install the newest version.</param> /// <returns><see langword="true"/> if the package was installed successfully; otherwise, <see langword="false"/>.</returns> public static Boolean InstallNuGetPackage(IExpressionCompilerState state, String packageID, String version = null) { Contract.Require(state, nameof(state)); Contract.RequireNotEmpty(packageID, nameof(packageID)); if (!IsNuGetAvailable()) { throw new PlatformNotSupportedException(); } Debug.Write("NuGet: Installing package " + packageID + "... "); var dir = Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); var exe = Path.Combine(dir, "nuget.exe"); var args = String.IsNullOrEmpty(version) ? $"install {packageID} -Source nuget.org" : $"install {packageID} -Source nuget.org -Version {version}"; var proc = new Process() { StartInfo = new ProcessStartInfo() { FileName = exe, Arguments = args, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, WorkingDirectory = state.GetWorkingDirectory() } }; proc.Start(); var exited = proc.WaitForExit((Int32)TimeSpan.FromMinutes(5).TotalSeconds); Debug.WriteLine("done."); return(exited && proc.ExitCode == 0); }
/// <summary> /// Creates an instance of <see cref="DataSourceWrapperInfo"/> for each of the specified data source definitions. /// </summary> /// <param name="state">The expression compiler's current state.</param> /// <param name="dataSourceDefinitions">The collection of <see cref="DataSourceDefinition"/> objects for which to create <see cref="DataSourceWrapperInfo"/> instances.</param> /// <returns>A collection containing the <see cref="DataSourceWrapperInfo"/> instances which were created.</returns> private static IEnumerable <DataSourceWrapperInfo> RetrieveDataSourceWrapperInfos(IExpressionCompilerState state, IEnumerable <DataSourceDefinition> dataSourceDefinitions) { var dataSourceWrapperInfos = new ConcurrentBag <DataSourceWrapperInfo>(); Parallel.ForEach(dataSourceDefinitions, viewDefinition => { var dataSourceWrapperInfo = GetDataSourceWrapperInfo(state, viewDefinition); if (dataSourceWrapperInfo == null) { return; } dataSourceWrapperInfos.Add(dataSourceWrapperInfo); }); return(dataSourceWrapperInfos); }
/// <summary> /// Creates a new instance of <see cref="DataSourceWrapperInfo"/> that represents the specified data source wrapper. /// </summary> /// <param name="state">The expression compiler's current state.</param> /// <param name="dataSourceDefinition">The data source definition for which to retrieve data source wrapper info.</param> /// <returns>The <see cref="DataSourceWrapperInfo"/> that was created to represent the specified data source.</returns> public static DataSourceWrapperInfo GetDataSourceWrapperInfo(IExpressionCompilerState state, DataSourceDefinition dataSourceDefinition) { var dataSourceWrappedType = dataSourceDefinition.TemplatedControl; if (dataSourceWrappedType == null) { var definedDataSourceTypeAttr = dataSourceDefinition.Definition.Attribute("ViewModelType"); var definedDataSourceTypeName = (String)definedDataSourceTypeAttr; if (definedDataSourceTypeName == null) { return(null); } var typeNameCommaIx = definedDataSourceTypeName.IndexOf(','); if (typeNameCommaIx < 0) { throw new BindingExpressionCompilationErrorException(definedDataSourceTypeAttr, dataSourceDefinition.DefinitionPath, CompilerStrings.ViewModelTypeIsNotFullyQualified.Format(definedDataSourceTypeName)); } var definedDataSourceType = Type.GetType(definedDataSourceTypeName); if (definedDataSourceType == null) { throw new BindingExpressionCompilationErrorException(definedDataSourceTypeAttr, dataSourceDefinition.DefinitionPath, PresentationStrings.ViewModelTypeNotFound.Format(definedDataSourceTypeName)); } dataSourceWrappedType = definedDataSourceType; } var dataSourceWrapperName = dataSourceDefinition.DataSourceWrapperName; var dataSourceWrapperExpressions = new List <BindingExpressionInfo>(); foreach (var element in dataSourceDefinition.Definition.Elements()) { FindBindingExpressionsInDataSource(state, dataSourceDefinition, dataSourceWrappedType, element, dataSourceWrapperExpressions); } dataSourceWrapperExpressions = CollapseDataSourceExpressions(dataSourceWrapperExpressions); var dataSourceReferences = new List <String>(); var dataSourceImports = new List <String>(); var xmlRoot = dataSourceDefinition.Definition.Parent; var xmlDirectives = xmlRoot.Elements("Directive"); foreach (var xmlDirective in xmlDirectives) { var xmlDirectiveType = (String)xmlDirective.Attribute("Type"); if (String.IsNullOrEmpty(xmlDirectiveType)) { throw new BindingExpressionCompilationErrorException(xmlDirective, dataSourceDefinition.DefinitionPath, CompilerStrings.ViewDirectiveMustHaveType); } var xmlDirectiveTypeName = xmlDirectiveType.ToLowerInvariant(); var xmlDirectiveValue = xmlDirective.Value.Trim(); switch (xmlDirectiveTypeName) { case "import": { if (String.IsNullOrEmpty(xmlDirectiveValue)) { throw new BindingExpressionCompilationErrorException(xmlDirective, dataSourceDefinition.DefinitionPath, CompilerStrings.ViewDirectiveHasInvalidValue); } dataSourceImports.Add(xmlDirective.Value.Trim()); } break; case "reference": { if (String.IsNullOrEmpty(xmlDirectiveValue)) { throw new BindingExpressionCompilationErrorException(xmlDirective, dataSourceDefinition.DefinitionPath, CompilerStrings.ViewDirectiveHasInvalidValue); } dataSourceReferences.Add(xmlDirective.Value.Trim()); } break; default: throw new BindingExpressionCompilationErrorException(xmlDirective, dataSourceDefinition.DefinitionPath, CompilerStrings.ViewDirectiveNotRecognized.Format(xmlDirectiveTypeName)); } } var frameworkTemplates = new Dictionary <String, XElement>(); FindFrameworkTemplateElements(dataSourceDefinition.Definition, frameworkTemplates); var frameworkTemplateWrapperDefs = frameworkTemplates.Select(x => DataSourceDefinition.FromView(dataSourceDefinition.DataSourceWrapperNamespace, x.Key, dataSourceDefinition.DefinitionPath, x.Value)).ToList(); var frameworkTemplateWrapperInfos = frameworkTemplateWrapperDefs.Select(definition => GetDataSourceWrapperInfoForFrameworkTemplate(state, dataSourceReferences, dataSourceImports, definition)).ToList(); return(new DataSourceWrapperInfo() { References = dataSourceReferences, Imports = dataSourceImports, DataSourceDefinition = dataSourceDefinition, DataSourcePath = dataSourceDefinition.DefinitionPath, DataSourceType = dataSourceWrappedType, DataSourceWrapperName = dataSourceWrapperName, Expressions = dataSourceWrapperExpressions, DependentWrapperInfos = frameworkTemplateWrapperInfos }); }
/// <summary> /// Searches the specified XML element tree for binding expressions and adds them to the specified collection. /// </summary> /// <param name="state">The expression compiler's current state.</param> /// <param name="dataSourceDefinition">The data source definition for the data source which is being compiled.</param> /// <param name="dataSourceWrappedType">The type for which a data source wrapper is being compiled.</param> /// <param name="element">The root of the XML element tree to search.</param> /// <param name="expressions">The list to populate with any binding expressions that are found.</param> private static void FindBindingExpressionsInDataSource(IExpressionCompilerState state, DataSourceDefinition dataSourceDefinition, Type dataSourceWrappedType, XElement element, List <BindingExpressionInfo> expressions) { var templateAnnotation = element.Annotation <FrameworkTemplateNameAnnotation>(); if (templateAnnotation != null) { return; } var elementName = element.Name.LocalName; var elementType = UvmlTypeAnalysis.GetPlaceholderType(dataSourceWrappedType, elementName); if (elementType != null || state.GetKnownType(elementName, out elementType)) { var attrs = Enumerable.Union( element.Attributes().Select(x => new { Object = (XObject)x, Name = x.Name.LocalName, Value = x.Value }), element.Elements().Where(x => x.Name.LocalName.StartsWith(elementName + ".")).Select(x => new { Object = (XObject)x, Name = x.Name.LocalName, Value = x.Value })); foreach (var attr in attrs) { var attrValue = attr.Value; if (!BindingExpressions.IsBindingExpression(attrValue)) { continue; } var dprop = FindDependencyOrAttachedPropertyByName(state, attr.Name, elementType); if (dprop == null) { throw new BindingExpressionCompilationErrorException(attr.Object, dataSourceDefinition.DefinitionPath, CompilerStrings.OnlyDependencyPropertiesCanBeBound.Format(attr.Name)); } var expText = BindingExpressions.GetBindingMemberPathPart(attrValue); var expProp = GetBindablePropertyOnDataSource(dataSourceWrappedType, expText); var expType = expProp?.PropertyType ?? dprop.PropertyType; if (typeof(DataTemplate).IsAssignableFrom(expType)) { continue; } expressions.Add(new BindingExpressionInfo(attr.Object, attrValue, expType) { GenerateGetter = true }); } if (element.Nodes().Count() == 1) { var singleChild = element.Nodes().Single(); if (singleChild.NodeType == XmlNodeType.Text) { var elementValue = ((XText)singleChild).Value; if (BindingExpressions.IsBindingExpression(elementValue)) { String defaultProperty; if (!state.GetElementDefaultProperty(elementType, out defaultProperty) || defaultProperty == null) { throw new BindingExpressionCompilationErrorException(singleChild, dataSourceDefinition.DefinitionPath, CompilerStrings.ElementDoesNotHaveDefaultProperty.Format(elementType.Name)); } var dprop = FindDependencyOrAttachedPropertyByName(state, defaultProperty, elementType); if (dprop == null) { throw new BindingExpressionCompilationErrorException(singleChild, dataSourceDefinition.DefinitionPath, CompilerStrings.OnlyDependencyPropertiesCanBeBound.Format(defaultProperty)); } var expText = BindingExpressions.GetBindingMemberPathPart(elementValue); var expProp = GetBindablePropertyOnDataSource(dataSourceWrappedType, expText); expressions.Add(new BindingExpressionInfo(singleChild, elementValue, expProp?.PropertyType ?? dprop.PropertyType) { GenerateGetter = true }); } } } } var children = element.Elements(); foreach (var child in children) { FindBindingExpressionsInDataSource(state, dataSourceDefinition, dataSourceWrappedType, child, expressions); } }
/// <summary> /// Creates a new <see cref="DataSourceWrapperInfo"/> instance which represents a particular framework template. /// </summary> public static DataSourceWrapperInfo GetDataSourceWrapperInfoForFrameworkTemplate(IExpressionCompilerState state, IEnumerable <String> references, IEnumerable <String> imports, DataSourceDefinition definition) { var dataSourceWrappedTypeAttr = definition.Definition.Attribute("ViewModelType"); if (dataSourceWrappedTypeAttr == null) { throw new BindingExpressionCompilationErrorException(definition.Definition, definition.DefinitionPath, PresentationStrings.TemplateMustSpecifyViewModelType); } var dataSourceWrappedType = Type.GetType(dataSourceWrappedTypeAttr.Value, false); if (dataSourceWrappedType == null) { throw new BindingExpressionCompilationErrorException(dataSourceWrappedTypeAttr, definition.DefinitionPath, PresentationStrings.ViewModelTypeNotFound.Format(dataSourceWrappedTypeAttr.Value)); } var dataSourceDefinition = definition; var dataSourceWrapperName = definition.DataSourceWrapperName; var expressions = new List <BindingExpressionInfo>(); foreach (var element in dataSourceDefinition.Definition.Elements()) { FindBindingExpressionsInDataSource(state, dataSourceDefinition, dataSourceWrappedType, element, expressions); } expressions = CollapseDataSourceExpressions(expressions); return(new DataSourceWrapperInfo() { References = references, Imports = imports, DataSourceDefinition = dataSourceDefinition, DataSourceType = dataSourceWrappedType, DataSourceWrapperName = dataSourceWrapperName, Expressions = expressions }); }
/// <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(); } }