Exemplo n.º 1
0
        /// <summary>
        /// Generate a replacement .designer.cs file for the given markup file, overwriting the existing
        /// .designer.cs file if there is one.
        /// </summary>
        public static bool GenerateDesignerForFilename(ICompileContext compileContext, string filename, IEnumerable <TagRegistration> tagRegistrations, AssemblyLoader assemblies, string assemblyDirectory, string rootPath)
        {
            string designer;
            string designerFilename = filename + ".designer.cs";

            // Load the markup from the .aspx or .ascx file.
            MarkupReader markup = new MarkupReader();
            MarkupInfo   markupInfo;

            try
            {
                markupInfo = markup.LoadMarkup(compileContext, filename, tagRegistrations, assemblies, assemblyDirectory, rootPath);
            }
            catch (Exception e)
            {
                compileContext.Error("{0}: Failed to load markup file:\r\n{1}", filename, e.Message);
                compileContext.Verbose("Stopping file processing due to exception.  Stack trace:\r\n{0}", e.StackTrace);
                return(false);
            }

            // If we're not inheriting a real class, there's no reason for a designer file to exist.
            if (markupInfo.ClassType == null)
            {
                compileContext.Verbose("Skipping generating designer file because markup does not have an Inherits=\"...\" attribute.", filename);
                return(true);
            }

            // Generate the output text for the new .designer.cs file.
            try
            {
                DesignerWriter designerWriter = new DesignerWriter();
                designer = designerWriter.CreateDesigner(compileContext, markupInfo);
            }
            catch (Exception e)
            {
                compileContext.Error("{0}: Cannot regenerate designer file:\r\n{1}", filename, e.Message);
                compileContext.Verbose("Stopping file processing due to exception.  Stack trace:\r\n{0}", e.StackTrace);
                return(false);
            }

            // Save the output .designer.cs file to disk.
            try
            {
                File.WriteAllText(designerFilename, designer, Encoding.UTF8);
            }
            catch (Exception e)
            {
                compileContext.Error("{0}: Cannot open designer file for writing:\r\n{1}", designerFilename, e.Message);
                compileContext.Verbose("Stopping file processing due to exception.  Stack trace:\r\n{0}", e.StackTrace);
                return(false);
            }

            return(true);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Verify the current .designer.cs file for the given markup file.
        /// </summary>
        /// <returns>True if the file passes inspection, false if it fails.</returns>
        public static bool VerifyDesignerForFilename(ICompileContext compileContext, string filename, IEnumerable <TagRegistration> tagRegistrations, AssemblyLoader assemblies, string assemblyDirectory, string rootPath)
        {
            DesignerInfo designerInfo;
            string       designerFilename = filename + ".designer.cs";

            // Load the markup from the .aspx or .ascx file.
            MarkupReader markup = new MarkupReader();
            MarkupInfo   markupInfo;

            try
            {
                markupInfo = markup.LoadMarkup(compileContext, filename, tagRegistrations, assemblies, assemblyDirectory, rootPath);
            }
            catch (Exception e)
            {
                compileContext.Error("{0}: Failed to load markup file:\r\n{1}", filename, e.Message);
                compileContext.Verbose("Stopping file processing due to exception.  Stack trace:\r\n{0}", e.StackTrace);
                return(false);
            }

            if (markupInfo.ClassType == null)
            {
                compileContext.Verbose("Skipping verification of .designer file, because markup has no Inherits=\"...\" attribute and therefore has no .designer file.", filename);
                return(true);
            }

            compileContext.Verbose(string.Empty);

            // Read and parse the current .designer.cs file.
            try
            {
                DesignerReader designerReader = new DesignerReader();
                designerInfo = designerReader.LoadDesignerFile(compileContext, designerFilename);
            }
            catch (Exception e)
            {
                compileContext.Error("{0}: Cannot load designer file:\r\n{1}", filename, e.Message);
                compileContext.Verbose("Stopping file processing due to exception.  Stack trace:\r\n{0}", e.StackTrace);
                return(false);
            }

            compileContext.Verbose(string.Empty);

            // And finally compare the expectations of the markup against the reality of the .designer.cs file.
            return(CompareMarkupInfoToDesignerInfo(compileContext, filename, markupInfo, designerInfo));
        }
Exemplo n.º 3
0
        /// <summary>
        /// For the given set of .aspx or .ascx files, generate all of their designer files.
        /// </summary>
        /// <param name="compileContext">The context in which errors are to be reported.</param>
        /// <param name="filenames">The filenames to generate.</param>
        /// <param name="rootPath">The root disk path of the website (usually the same as the path to "web.config").</param>
        /// <param name="websiteDllFileName">The disk path to the website's DLL.</param>
        public static bool GenerateDesignerFiles(ICompileContext compileContext, IEnumerable <string> filenames, string rootPath, string websiteDllFileName)
        {
            int filenameCount = filenames.Count();

            compileContext.BeginTask(filenameCount);

            // Load and parse the "web.config".
            WebConfigReader webConfigReader = new WebConfigReader();

            try
            {
                webConfigReader.LoadWebConfig(compileContext, Path.Combine(rootPath, WebConfigFilename), rootPath);
            }
            catch (Exception e)
            {
                compileContext.Error("Cannot load {0}:\r\n{1}", Path.Combine(rootPath, WebConfigFilename), e.Message);
                return(false);
            }

            // Load any assemblies we know we'll need.  This includes the default assemblies, any declared
            // in the web.config, and, of course, the website's DLL itself.
            AssemblyLoader assemblyLoader = new AssemblyLoader();
            List <string>  assemblyNames  = new List <string>();

            assemblyNames.AddRange(_standardTagRegistrations.Where(r => !string.IsNullOrEmpty(r.AssemblyFilename)).Select(r => r.AssemblyFilename).Distinct());
            assemblyNames.AddRange(webConfigReader.TagRegistrations.Where(r => !string.IsNullOrEmpty(r.AssemblyFilename)).Select(r => r.AssemblyFilename).Distinct());
            string dllFullPath = Path.GetFullPath(websiteDllFileName);

            assemblyNames.Add(dllFullPath);
            string assemblyDirectory = Path.GetDirectoryName(dllFullPath);

            assemblyLoader.PreloadAssemblies(compileContext, assemblyNames, assemblyDirectory);
            assemblyLoader.PrimaryAssembly = assemblyLoader[dllFullPath];

            // Add the default tag registrations, including those from System.Web and any declared in the "web.config".
            List <TagRegistration> tagRegistrations = new List <TagRegistration>();

            tagRegistrations.AddRange(_standardTagRegistrations);
            tagRegistrations.AddRange(webConfigReader.TagRegistrations);

            // Spin through any user controls that were declared in the web.config and connect them to their actual
            // .NET class types via reflection.
            compileContext.Verbose("Resolving user controls declared in the web.config.");
            compileContext.VerboseNesting++;
            ResolveUserControls(compileContext, tagRegistrations, assemblyLoader, assemblyDirectory, rootPath, rootPath);
            compileContext.VerboseNesting--;
            compileContext.Verbose(string.Empty);

            // Now that all the setup is done, load and parse each individual markup file into its own .designer.cs output file.
            bool result = true;

            foreach (string filename in filenames)
            {
                compileContext.Verbose("Begin processing \"{0}\"...", filename);
                compileContext.Verbose("");
                compileContext.VerboseNesting++;

                compileContext.BeginFile(filename);
                bool succeeded = GenerateDesignerForFilename(compileContext, filename, tagRegistrations, assemblyLoader, assemblyDirectory, rootPath);
                result &= succeeded;
                compileContext.EndFile(filename, succeeded);

                compileContext.VerboseNesting--;
                compileContext.Verbose("");
                compileContext.Verbose("End processing \"{0}\".", filename);
            }
            return(result);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Compare a parsed markup file against a parsed designer file to determine if they match each other.
        /// </summary>
        /// <param name="compileContext">The context in which errors should be reported.</param>
        /// <param name="filename">The filename to use for reporting errors.</param>
        /// <param name="markupInfo">The markup file to compare.</param>
        /// <param name="designerInfo">The designer file to compare.</param>
        /// <returns>True if they match, false if they do not.</returns>
        private static bool CompareMarkupInfoToDesignerInfo(ICompileContext compileContext, string filename, MarkupInfo markupInfo, DesignerInfo designerInfo)
        {
            compileContext.Verbose("Comparing markup controls to .designer file properties...");

            compileContext.Verbose("Comparing classnames.");

            // First, make sure the type names match; we *should* be talking about the same classes here.
            if (markupInfo.ClassType == null)
            {
                compileContext.Error("{0}: Designer file exists, but markup file has no Inherits=\"...\" attribute.", filename);
                return(false);
            }
            if (markupInfo.ClassType.FullName != designerInfo.FullTypeName)
            {
                compileContext.Error("{0}: Designer file and markup file specify different type names (\"{1}\" in the markup, and \"{2}\" in the designer file.",
                                     filename, markupInfo.ClassType.FullName, designerInfo.FullTypeName);
                return(false);
            }

            // Build lookup tables for the property declarations in the designer file and in the markup file.
            // We'll use these to make searching for property matches that much faster, and to detect duplicates,
            // and to ensure that we're talking about the same set of properties in both files.

            compileContext.Verbose("Checking for duplicate control declarations.");

            Dictionary <string, OutputControl> markupPropertiesByName = new Dictionary <string, OutputControl>();
            Dictionary <string, DesignerPropertyDeclaration> designerProperiesByName = new Dictionary <string, DesignerPropertyDeclaration>();

            List <string> duplicateMarkupProperties = new List <string>();

            foreach (OutputControl outputControl in markupInfo.OutputControls)
            {
                if (string.IsNullOrEmpty(outputControl.Name))
                {
                    continue;
                }

                if (markupPropertiesByName.ContainsKey(outputControl.Name))
                {
                    duplicateMarkupProperties.Add(outputControl.Name);
                }
                else
                {
                    markupPropertiesByName.Add(outputControl.Name, outputControl);
                }
            }

            List <string> duplicateDesignerProperties = new List <string>();

            foreach (DesignerPropertyDeclaration propertyDeclaration in designerInfo.PropertyDeclarations)
            {
                if (designerProperiesByName.ContainsKey(propertyDeclaration.Name))
                {
                    duplicateDesignerProperties.Add(propertyDeclaration.Name);
                }
                else
                {
                    designerProperiesByName.Add(propertyDeclaration.Name, propertyDeclaration);
                }
            }

            // Check the lookup tables for duplicates.  There shouldn't be any.

            if (duplicateMarkupProperties.Count > 0)
            {
                compileContext.Error("{0}: Malformed markup error: Found multiple controls in the markup that have the same ID.  Stopping verification now due to invalid markup file.  Duplicate IDs: {1}",
                                     filename, Join(duplicateMarkupProperties, ", "));
            }
            if (duplicateDesignerProperties.Count > 0)
            {
                compileContext.Error("{0}: Malformed designer error: Found multiple property declarations in the .designer file that have the same name.  Stopping verification now due to invalid designer file.  Duplicate names: {1}",
                                     filename, Join(duplicateDesignerProperties, ", "));
            }
            if (duplicateMarkupProperties.Count > 0 || duplicateDesignerProperties.Count > 0)
            {
                return(false);
            }

            // Okay, now check to see if the markup or designer declare property names that the other doesn't have.

            compileContext.Verbose("Checking for missing control declarations.");

            Type          contentControl            = typeof(System.Web.UI.WebControls.Content);
            List <string> missingDesignerProperties = markupInfo.OutputControls
                                                      .Where(p => !string.IsNullOrEmpty(p.Name) && p.ReflectedControl.ControlType != contentControl && !designerProperiesByName.ContainsKey(p.Name))
                                                      .Select(p => p.Name)
                                                      .ToList();
            List <string> missingMarkupProperties = designerInfo.PropertyDeclarations
                                                    .Where(p => !string.IsNullOrEmpty(p.Name) && !markupPropertiesByName.ContainsKey(p.Name))
                                                    .Select(p => p.Name)
                                                    .ToList();

            if (missingDesignerProperties.Count > 0)
            {
                compileContext.Error("{0}: Missing property error: Found controls declared in the markup that do not exist in the .designer file.  Missing IDs: {1}",
                                     filename, Join(missingDesignerProperties, ", "));
            }
            if (missingMarkupProperties.Count > 0)
            {
                compileContext.Error("{0}: Missing control error: Found property declarations in the .designer file that have no control declaration in the markup.  Missing controls: {1}",
                                     filename, Join(missingMarkupProperties, ", "));
            }

            // We've now established that both files refer to the same set of names.  We now need to check
            // to make sure they all refer to the same control types.

            int numTypeMismatches = 0;

            compileContext.Verbose("Checking for type mismatches.");

            foreach (OutputControl outputControl in markupInfo.OutputControls)
            {
                if (string.IsNullOrEmpty(outputControl.Name) ||
                    outputControl.ReflectedControl.ControlType == contentControl ||
                    !designerProperiesByName.ContainsKey(outputControl.Name))
                {
                    continue;
                }

                DesignerPropertyDeclaration designerPropertyDeclaration = designerProperiesByName[outputControl.Name];
                if (designerPropertyDeclaration.PropertyTypeName != outputControl.ReflectedControl.ControlType.FullName)
                {
                    compileContext.Error("{0}: Type mismatch: Control \"{1}\" has type {2} in the markup but type {3} in the .designer file.",
                                         filename, outputControl.Name, outputControl.ReflectedControl.ControlType.FullName, designerPropertyDeclaration.PropertyTypeName);
                    numTypeMismatches++;
                }
            }

            if (missingDesignerProperties.Count > 0 || missingMarkupProperties.Count > 0 || numTypeMismatches > 0)
            {
                return(false);
            }

            // One last very touchy check:  All the properties exist in both files, and they have the same names and
            // same types --- but are they in the right order?  Visual Studio is very picky about the order, and if
            // they don't match, the Visual Studio designer will break.

            compileContext.Verbose("Checking for mis-ordered declarations.");

            for (int m = 0, d = 0; m < markupInfo.OutputControls.Count;)
            {
                OutputControl outputControl = markupInfo.OutputControls[m++];
                if (string.IsNullOrEmpty(outputControl.Name) ||
                    outputControl.ReflectedControl.ControlType == contentControl)
                {
                    continue;
                }

                DesignerPropertyDeclaration designerPropertyDeclaration = designerInfo.PropertyDeclarations[d++];

                if (designerPropertyDeclaration.Name != outputControl.Name ||
                    designerPropertyDeclaration.PropertyTypeName != outputControl.ReflectedControl.ControlType.FullName)
                {
                    compileContext.Error("{0}: Ordering error: All of the same controls exist in both the markup and the .designer file, but they do not appear in the same order.", filename);
                    return(false);
                }
            }

            compileContext.Verbose("{0}: Success!", filename);
            return(true);
        }