Example #1
0
        /// <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(ExpressionCompilerState 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 = ExpressionCompiler.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_GetElementByName<{0}>(\"{1}\").", 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(ExpressionCompilerState 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"));
            return templateDefs.ToList();
        }
Example #3
0
        /// <summary>
        /// Writes the source code for an individual data source wrapper class.
        /// </summary>
        private static void WriteSourceCodeForDataSourceWrapperClass(ExpressionCompilerState state,
            DataSourceWrapperInfo dataSourceWrapperInfo, DataSourceWrapperWriter writer)
        {
            // Class declaration
            writer.WriteLine("[System.CLSCompliant(false)]");
            writer.WriteLine("[System.CodeDom.Compiler.GeneratedCode(\"UPF Binding Expression Compiler\", \"{0}\")]", FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion);
            writer.WriteLine("public sealed partial class {0} : {1}", dataSourceWrapperInfo.DataSourceWrapperName, writer.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 (!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 (!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 (!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");

            // Class complete
            writer.WriteLine("}");
        }
Example #4
0
        /// <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(ExpressionCompilerState 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(" + GetCSharpTypeName(dprop.OwnerType) + ")" : "null",
                      isSimpleDependencyProperty ? "\"" + dprop.Name + "\"" : "null");

            WriteLine("public {0} __UPF_Expression{1}", 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,
                                           GetCSharpTypeName(dprop.PropertyType),
                                           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});", GetCSharpTypeName(expressionInfo.Type), expFormatString);
                    WriteLine("}");
                }
                else
                {
                    WriteLine("get {{ return ({0})({1}); }}", 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, GetCSharpTypeName(dprop.PropertyType), GetCSharpTypeName(dprop.OwnerType), dpropField.Name);
                        WriteLine("{0}SetValue<{1}>({2}.{3}, __UPF_ConvertFromString(value.ToString(), current));",
                                  expTarget, GetCSharpTypeName(dprop.PropertyType), 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,
                                      GetCSharpTypeName(dprop.PropertyType),
                                      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,
                                      GetCSharpTypeName(dprop.PropertyType),
                                      GetCSharpTypeName(dprop.OwnerType),
                                      dpropField.Name, targetTypeName);
                        }
                    }
                }
                else
                {
                    if (expressionInfo.Type == typeof(String) || expressionInfo.Type == typeof(VersionedStringSource))
                    {
                        WriteLine("set");
                        WriteLine("{");
                        WriteLine("var current = {0}{1};", expTarget, expText);
                        WriteLine("{0}{1} = __UPF_ConvertFromString(value.ToString(), 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, 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("}");
        }
 /// <summary>
 /// Gets the working directory for the specified compilation.
 /// </summary>
 /// <param name="state">The expression compiler's current state.</param>
 /// <returns>The working directory for the specified compilation.</returns>
 private static String GetWorkingDirectory(ExpressionCompilerState state)
 {
     return state.WorkInTemporaryDirectory ? Path.Combine(Path.GetTempPath(), "UV_CompiledExpressions") : "UV_CompiledExpressions";
 }
        /// <summary>
        /// Writes any compiler errors to the working directory.
        /// </summary>
        /// <param name="state">The expression compiler's current state.</param>
        /// <param name="models">The list of models which were compiled.</param>
        /// <param name="results">The results of the previous compilation pass.</param>
        private static void WriteErrorsToWorkingDirectory(ExpressionCompilerState state, IEnumerable<DataSourceWrapperInfo> models, CompilerResults results)
        {
            var logpath = Path.Combine(GetWorkingDirectory(state), "Compilation Errors.txt");
			try
			{
				File.Delete(logpath);
			}
			catch(DirectoryNotFoundException) {	}

			var logdir = Path.GetDirectoryName(logpath);
			Directory.CreateDirectory(logdir);

			// NOTE: Under Mono we seem to get warnings even when "Treat Warnings as Errors" is turned off.
			var trueErrors = results.Errors.Cast<CompilerError>().Where(x => !x.IsWarning).ToList();
			if (trueErrors.Count > 0)
            {
                var filesWithErrors = trueErrors.Select(x => x.FileName).Where(x => !String.IsNullOrEmpty(x)).Distinct();
                var filesWithErrorsPretty = new Dictionary<String, String> { { String.Empty, String.Empty } };
                
                foreach (var fileWithErrors in filesWithErrors)
                {
                    var modelNameForFile = models.Where(x => x.UniqueID.ToString() == Path.GetFileNameWithoutExtension(fileWithErrors))
                        .Select(x => x.DataSourceWrapperName).SingleOrDefault();

                    var prettyFileName = Path.ChangeExtension(modelNameForFile, "cs");
                    filesWithErrorsPretty[fileWithErrors] = prettyFileName;

                    var fileWithErrorsSrc = Path.GetFullPath(fileWithErrors);
                    var fileWithErrorsDst = Path.Combine(GetWorkingDirectory(state), prettyFileName);
                    File.Copy(fileWithErrorsSrc, fileWithErrorsDst, true);
                }

				var errorStrings = trueErrors.Select(x =>
                    String.Format("{0}\t{1}\t{2}\t{3}", x.ErrorNumber, x.ErrorText, filesWithErrorsPretty[x.FileName ?? String.Empty], x.Line));
                
                File.WriteAllLines(logpath, Enumerable.Union(new[] { "Code\tDescription\tFile\tLine" }, errorStrings));
            }
        }
 /// <summary>
 /// Deletes the compiler's working directory.
 /// </summary>
 /// <param name="state">The expression compiler's current state.</param>
 private static void DeleteWorkingDirectory(ExpressionCompilerState state)
 {
     try
     {
         Directory.Delete(GetWorkingDirectory(state), true);
     }
     catch (IOException) { }
 }
        /// <summary>
        /// Writes the source code for the specified data source wrapper.
        /// </summary>
        /// <param name="state">The expression compiler's current state.</param>
        /// <param name="dataSourceWrapperInfo">The <see cref="DataSourceWrapperInfo"/> that describes the data source wrapper being generated.</param>
        private static void WriteSourceCodeForDataSourceWrapper(ExpressionCompilerState 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 and class declaration
                var @namespace = dataSourceWrapperInfo.DataSourceDefinition.DataSourceWrapperNamespace;
                writer.WriteLine("namespace " + @namespace);
                writer.WriteLine("{");
                writer.WriteLine("#pragma warning disable 1591");
                writer.WriteLine("#pragma warning disable 0184");
                writer.WriteLine("[System.CLSCompliant(false)]");
                writer.WriteLine("[System.CodeDom.Compiler.GeneratedCode(\"UPF Binding Expression Compiler\", \"{0}\")]", FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion);
                writer.WriteLine("public sealed partial class {0} : {1}", dataSourceWrapperInfo.DataSourceWrapperName, writer.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 (!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 (!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 (!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");

                // Source code generation complete
                writer.WriteLine("}");
                writer.WriteLine("#pragma warning restore 0184");
                writer.WriteLine("#pragma warning restore 1591");
                writer.WriteLine("}");

                dataSourceWrapperInfo.DataSourceWrapperSourceCode = writer.ToString();
            }
        }
        /// <summary>
        /// Compiles the specified collection of view models.
        /// </summary>
        private static BindingExpressionCompilationResult CompileViewModels(ExpressionCompilerState state, IEnumerable<DataSourceWrapperInfo> models, String output)
        {
            DeleteWorkingDirectory(state);

            var referencedAssemblies = GetDefaultReferencedAssemblies();

            var expressionVerificationResult = 
                PerformExpressionVerificationCompilationPass(state, models, referencedAssemblies);

			if (expressionVerificationResult.Errors.Cast<CompilerError>().Where(x => !x.IsWarning).Any())
            {
                if (state.WriteErrorsToFile)
                    WriteErrorsToWorkingDirectory(state, models, expressionVerificationResult);

                return BindingExpressionCompilationResult.CreateFailed(CompilerStrings.FailedExpressionValidationPass,
                    CreateBindingExpressionCompilationErrors(state, models, expressionVerificationResult.Errors));
            }

            var setterEliminationPassResult =
                PerformSetterEliminationCompilationPass(state, models, referencedAssemblies);
            
            var conversionFixupPassResult =
                PerformConversionFixupCompilationPass(state, models, referencedAssemblies, setterEliminationPassResult);

            var finalPassResult = 
                PerformFinalCompilationPass(state, output, models, referencedAssemblies, conversionFixupPassResult);

			if (finalPassResult.Errors.Cast<CompilerError>().Where(x => !x.IsWarning).Any())
            {
                if (state.WriteErrorsToFile)
                    WriteErrorsToWorkingDirectory(state, models, finalPassResult);

                return BindingExpressionCompilationResult.CreateFailed(CompilerStrings.FailedFinalPass,
                    CreateBindingExpressionCompilationErrors(state, models, finalPassResult.Errors));
            }

            return BindingExpressionCompilationResult.CreateSucceeded();
        }
        /// <summary>
        /// Creates a new instance of <see cref="ExpressionCompilerState"/> from the specified set of compiler options.
        /// </summary>
        private ExpressionCompilerState CreateCompilerState(UltravioletContext uv, BindingExpressionCompilerOptions options)
        {
            var compiler = CreateCodeProvider();
            var state = new ExpressionCompilerState(uv, compiler);
            state.WorkInTemporaryDirectory = options.WorkInTemporaryDirectory;
            state.WriteErrorsToFile = options.WriteErrorsToFile;

            return state;
        }
        /// <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(ExpressionCompilerState 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 = ExpressionCompiler.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_GetElementByName<{0}>(\"{1}\").", GetCSharpTypeName(expTargetType), expPartTarget);

                return true;
            }

            expTarget = default(String);
            expTargetType = dataSourceWrapperInfo.DataSourceType;

            return false;
        }
        /// <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(ExpressionCompilerState 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(" + GetCSharpTypeName(dprop.OwnerType) + ")" : "null",
                isSimpleDependencyProperty ? "\"" + dprop.Name + "\"" : "null");

            WriteLine("public {0} __UPF_Expression{1}", 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,
                       GetCSharpTypeName(dprop.PropertyType),
                       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});", GetCSharpTypeName(expressionInfo.Type), expFormatString);
                    WriteLine("}");
                }
                else
                {
                    WriteLine("get {{ return ({0})({1}); }}", 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, GetCSharpTypeName(dprop.PropertyType), GetCSharpTypeName(dprop.OwnerType), dpropField.Name);
                        WriteLine("{0}SetValue<{1}>({2}.{3}, __UPF_ConvertFromString(value, current));",
                            expTarget, GetCSharpTypeName(dprop.PropertyType), 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,
                                GetCSharpTypeName(dprop.PropertyType),
                                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,
                                GetCSharpTypeName(dprop.PropertyType),
                                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, 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("}");
        }
Example #13
0
        /// <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(ExpressionCompilerState 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;
        }
Example #14
0
        /// <summary>
        /// Creates a new <see cref="DataSourceWrapperInfo"/> instance which represents a particular framework template.
        /// </summary>
        private static DataSourceWrapperInfo GetDataSourceWrapperInfoForFrameworkTemplate(ExpressionCompilerState 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>
        /// 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(ExpressionCompilerState 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>
        private static DataSourceWrapperInfo GetDataSourceWrapperInfo(ExpressionCompilerState 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));
                }
            }

            return new DataSourceWrapperInfo()
            {
                References = dataSourceReferences,
                Imports = dataSourceImports,
                DataSourceDefinition = dataSourceDefinition,
                DataSourceType = dataSourceWrappedType,
                DataSourceWrapperName = dataSourceWrapperName,
                Expressions = dataSourceWrapperExpressions,
            };
        }
        /// <summary>
        /// Performs the first compilation pass, which generates expression getters in order to verify that the expressions are valid code.
        /// </summary>
        private static CompilerResults PerformExpressionVerificationCompilationPass(ExpressionCompilerState state, IEnumerable<DataSourceWrapperInfo> models, ConcurrentBag<String> referencedAssemblies)
        {
            Parallel.ForEach(models, model =>
            {
                referencedAssemblies.Add(model.DataSourceType.Assembly.Location);
                foreach (var reference in model.References)
                {
                    referencedAssemblies.Add(reference);
                }

                foreach (var expression in model.Expressions)
                {
                    expression.GenerateGetter = true;
                    expression.GenerateSetter = false;
                }
                
                WriteSourceCodeForDataSourceWrapper(state, model);
            });

            return CompileDataSourceWrapperSources(state, null, models, referencedAssemblies);
        }
        /// <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(ExpressionCompilerState state, DataSourceDefinition dataSourceDefinition,
            Type dataSourceWrappedType, XElement element, List<BindingExpressionInfo> expressions)
        {
            var elementName = element.Name.LocalName;
            var elementType = GetPlaceholderType(dataSourceWrappedType, elementName);            
            if (elementType != null || state.GetKnownType(elementName, out elementType))
            {
                var attrs = element.Attributes();
                foreach (var attr in attrs)
                {
                    var attrValue = attr.Value;
                    if (!BindingExpressions.IsBindingExpression(attrValue))
                        continue;
                    
                    var dprop = FindDependencyOrAttachedPropertyByName(state, attr.Name.LocalName, elementType);
                    if (dprop == null)
                    {
                        throw new BindingExpressionCompilationErrorException(attr, dataSourceDefinition.DefinitionPath,
                            CompilerStrings.OnlyDependencyPropertiesCanBeBound.Format(attr.Name.LocalName));
                    }

                    expressions.Add(new BindingExpressionInfo(attr, attrValue, dprop.PropertyType) { 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))
                            {
                                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));
                            }

                            expressions.Add(new BindingExpressionInfo(singleChild, elementValue, dprop.PropertyType) { GenerateGetter = true });
                        }
                    }
                }
            }

            var children = element.Elements();
            foreach (var child in children)
            {
                FindBindingExpressionsInDataSource(state, dataSourceDefinition, 
                    dataSourceWrappedType, child, expressions);
            }
        }
        /// <summary>
        /// Performs the second compilation pass, which generates setters in order to determine which expressions support two-way bindings.
        /// </summary>
        private static CompilerResults PerformSetterEliminationCompilationPass(ExpressionCompilerState state, IEnumerable<DataSourceWrapperInfo> models, ConcurrentBag<String> referencedAssemblies)
        {
            Parallel.ForEach(models, model =>
            {
                foreach (var expression in model.Expressions)
                {
                    expression.GenerateGetter = true;
                    expression.GenerateSetter = true;
                }

                WriteSourceCodeForDataSourceWrapper(state, model);
            });
            return CompileDataSourceWrapperSources(state, null, models, referencedAssemblies);
        }
        /// <summary>
        /// Writes the source code of the specified collection of wrappers to the working directory.
        /// </summary>
        /// <param name="state">The expression compiler's current state.</param>
        /// <param name="models">The list of models which were compiled.</param>
        private static void WriteCompiledFilesToWorkingDirectory(ExpressionCompilerState state, IEnumerable<DataSourceWrapperInfo> models)
        {
            var workingDirectory = GetWorkingDirectory(state);
            Directory.CreateDirectory(workingDirectory);

            foreach (var model in models)
            {
                var path = Path.ChangeExtension(Path.Combine(workingDirectory, model.DataSourceWrapperName), "cs");
                File.WriteAllText(path, model.DataSourceWrapperSourceCode);
            }
        }
        /// <summary>
        /// Performs the third compilation pass, which attempts to fix any errors caused by non-implicit conversions and nullable types that need to be cast to non-nullable types.
        /// </summary>
        private static CompilerResults PerformConversionFixupCompilationPass(ExpressionCompilerState state, IEnumerable<DataSourceWrapperInfo> models, ConcurrentBag<String> referencedAssemblies, CompilerResults setterEliminationResult)
        {
            var errors = setterEliminationResult.Errors.Cast<CompilerError>().ToList();

            var fixableErrorNumbers = new List<String> 
            {
                "CS0266",
                "CS1502",
                "CS1503"
            };

            Parallel.ForEach(models, model =>
            {
                var dataSourceWrapperFilename = Path.GetFileName(GetWorkingFileForDataSourceWrapper(model));
                var dataSourceWrapperErrors = errors.Where(x => Path.GetFileName(x.FileName) == dataSourceWrapperFilename).ToList();

                foreach (var expression in model.Expressions)
                {
                    var setterErrors = dataSourceWrapperErrors.Where(x => x.Line >= expression.SetterLineStart && x.Line <= expression.SetterLineEnd).ToList();
                    var setterIsNullable = Nullable.GetUnderlyingType(expression.Type) != null;

                    expression.GenerateSetter = !setterErrors.Any() || (setterIsNullable && setterErrors.All(x => fixableErrorNumbers.Contains(x.ErrorNumber)));
                    expression.NullableFixup  = setterIsNullable;

                    if (setterErrors.Count == 1 && setterErrors.Single().ErrorNumber == "CS0266")
                    {
                        var error = setterErrors.Single();
                        var match = regexCS0266.Match(error.ErrorText);
                        expression.CS0266SourceType = match.Groups["source"].Value;
                        expression.CS0266TargetType = match.Groups["target"].Value;
                        expression.GenerateSetter = true;
                    }
                }

                WriteSourceCodeForDataSourceWrapper(state, model);
            });
            return CompileDataSourceWrapperSources(state, null, models, referencedAssemblies);
        }
        /// <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 <c>null</c> if there is no such dependency property.</returns>
        private static DependencyProperty FindDependencyOrAttachedPropertyByName(ExpressionCompilerState state, String name, Type ownerType)
        {
            String container;
            String property;
            if (IsAttachedProperty(name, out container, out property))
            {
                Type containerType;
                if (!state.GetKnownType(container, out containerType))
                    return null;

                return DependencyProperty.FindByName(property, containerType);
            }
            return DependencyProperty.FindByName(name, ownerType);
        }
        /// <summary>
        /// Performs the final compilation pass, which removes invalid expression setters based on the results of the previous pass.
        /// </summary>
        private static CompilerResults PerformFinalCompilationPass(ExpressionCompilerState state, String output, IEnumerable<DataSourceWrapperInfo> models, ConcurrentBag<String> referencedAssemblies, CompilerResults nullableFixupResult)
        {
            var errors = nullableFixupResult.Errors.Cast<CompilerError>().ToList();

            Parallel.ForEach(models, model =>
            {
                var dataSourceWrapperFilename = Path.GetFileName(GetWorkingFileForDataSourceWrapper(model));
                var dataSourceWrapperErrors = errors.Where(x => Path.GetFileName(x.FileName) == dataSourceWrapperFilename).ToList();

                foreach (var expression in model.Expressions)
                {
                    if (expression.GenerateSetter && dataSourceWrapperErrors.Any(x => x.Line >= expression.SetterLineStart && x.Line <= expression.SetterLineEnd))
                    {
                        expression.GenerateSetter = false;
                    }
                }

                WriteSourceCodeForDataSourceWrapper(state, model);
            });

            return CompileDataSourceWrapperSources(state, output, models, referencedAssemblies);
        }
        /// <summary>
        /// Converts a <see cref="CompilerErrorCollection"/> to a collection of <see cref="BindingExpressionCompilationError"/> objects.
        /// </summary>
        /// <param name="state">The expression compiler's current state.</param>
        /// <param name="models">The list of models which were compiled.</param>
        /// <param name="errors">The collection of errors produced during compilation.</param>
        /// <returns>A list containing the converted errors.</returns>
        private static List<BindingExpressionCompilationError> CreateBindingExpressionCompilationErrors(ExpressionCompilerState state,
            IEnumerable<DataSourceWrapperInfo> models, CompilerErrorCollection errors)
        {
            var result = new List<BindingExpressionCompilationError>();

            var workingDirectory = GetWorkingDirectory(state);

            var errorsByFile = errors.Cast<CompilerError>()
                .Where(x => !x.IsWarning).GroupBy(x => Path.GetFileName(x.FileName)).ToDictionary(x => x.Key, x => x.ToList());

            foreach (var model in models)
            {
                var dataSourceWrapperFilename = Path.GetFileName(GetWorkingFileForDataSourceWrapper(model));
                var dataSourceErrors = default(List<CompilerError>);
                if (errorsByFile.TryGetValue(dataSourceWrapperFilename, out dataSourceErrors))
                {
                    foreach (var dataSourceError in dataSourceErrors)
                    {
                        var fullPathToFile = model.DataSourceWrapperName;
                        if (state.WriteErrorsToFile)
                        {
                            fullPathToFile = Path.GetFullPath(Path.Combine(workingDirectory, 
                                Path.ChangeExtension(model.DataSourceWrapperName, "cs")));
                        }

                        result.Add(new BindingExpressionCompilationError(fullPathToFile,
                            dataSourceError.Line, dataSourceError.Column, dataSourceError.ErrorNumber, dataSourceError.ErrorText));
                    }
                }
            }

            return result;
        }
        /// <summary>
        /// Compiles the specified data source wrapper sources into a managed assembly.
        /// </summary>
        /// <param name="state">The expression compiler's current state.</param>
        /// <param name="output">The path to the assembly file which will be created.</param>
        /// <param name="infos">A collection of <see cref="DataSourceWrapperInfo"/> instances containing the source code to compile.</param>
        /// <param name="references">A collection of assembly locations which should be referenced by the compiled assembly.</param>
        /// <returns>A <see cref="CompilerResults"/> instance that represents the result of compilation.</returns>
        private static CompilerResults CompileDataSourceWrapperSources(ExpressionCompilerState state, String output, IEnumerable<DataSourceWrapperInfo> infos, IEnumerable<String> references)
        {
            var options = new CompilerParameters();
            options.OutputAssembly = output;
            options.GenerateExecutable = false;
            options.GenerateInMemory = true;
            options.IncludeDebugInformation = false;
			options.TreatWarningsAsErrors = false;
            options.ReferencedAssemblies.AddRange(references.Distinct().ToArray());

            var files = new List<String>();

            foreach (var info in infos)
            {
                var path = GetWorkingFileForDataSourceWrapper(info);
                files.Add(path);

                File.WriteAllText(path, info.DataSourceWrapperSourceCode);
            }
            
            return state.Compiler.CompileAssemblyFromFile(options, files.ToArray());
        }
        /// <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>
        private static IEnumerable<DataSourceWrapperInfo> GetDataSourceWrapperInfos(ExpressionCompilerState state, UltravioletContext uv, 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);
        }
Example #27
0
        /// <summary>
        /// Writes the source code for the specified data source wrapper.
        /// </summary>
        private static void WriteSourceCodeForDataSourceWrapper(ExpressionCompilerState 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();
            }
        }