Ejemplo n.º 1
0
        public static void Generate(
            string outputExcelFilePath,
            FeaturesAndEstimationsFileProcessor featuresAndEstimationsFileProcessor,
            SortedDictionary <Tuple <string, string>, HashSet <string> > unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed,
            IEnumerable <string> analyzedDlls)
        {
            ExcelEngine excelEngine = new ExcelEngine();

            //Instantiate the Excel application object
            IApplication application = excelEngine.Excel;

            //Assigns default application version
            application.DefaultVersion = ExcelVersion.Excel2013;

            //A new workbook is created equivalent to creating a new workbook in Excel
            //Create a workbook with 1 worksheet
            IWorkbook workbook = application.Workbooks.Create(1);

            //Access first worksheet from the workbook
            IWorksheet worksheet = workbook.Worksheets[0];

            // Set the columns width (note: columns here are one-based):
            worksheet.SetColumnWidth(3, 60);
            worksheet.SetColumnWidth(4, 45);
            worksheet.SetColumnWidth(5, 30);
            worksheet.SetColumnWidth(6, 13);

            // Prepare the styles:
            IStyle style_Remove, style_EasyWorkaround, style_RequiresImplementation, style_NonTrivialWorkaround;

            PrepareTheStyles(workbook, out style_Remove, out style_EasyWorkaround, out style_RequiresImplementation, out style_NonTrivialWorkaround);

            // Set the header text:
            worksheet.Range[5, 3].Text = "FEATURE:";
            worksheet.Range[5, 4].Text = "CLASSES OR FILES WHERE THE FEATURE IS USED:";

            // Set the header format:
            worksheet.Range[5, 3, 5, 6].CellStyle.Font.Bold = true;

            // Index of the first row that contains the feature:
            int currentRow = 7;

            var groupedResult = unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.GroupBy((element) => element.Key.Item2);

            foreach (IGrouping <string, KeyValuePair <Tuple <string, string>, HashSet <string> > > item in groupedResult)
            {
                var currentCell = worksheet.Range["A" + currentRow.ToString()];
                currentCell.Text = "From \"" + item.Key + "\":";
                currentCell.CellStyle.Font.Bold      = true;
                currentCell.CellStyle.Font.Size      = 14;
                currentCell.CellStyle.Font.Underline = ExcelUnderline.Single;
                currentRow += 2;
                foreach (KeyValuePair <Tuple <string, string>, HashSet <string> > obj in item)
                {
                    // Feature name:
                    string featureName     = obj.Key.Item1;
                    IRange featureNameCell = worksheet.Range["C" + currentRow.ToString()];
                    featureNameCell.Text = featureName;

                    // Locations where the feature is used:
                    List <string> sortedLocations = new List <string>(obj.Value);
                    sortedLocations.Sort();
                    string sortLocationsJoined = string.Join(", ", sortedLocations);
                    IRange locationCell        = worksheet.Range["D" + currentRow.ToString()];
                    locationCell.Text = sortLocationsJoined;

                    // Other information:
                    ExcelRowInfo excelRowInfo;
                    if (featuresAndEstimationsFileProcessor.TryGetInfo(featureName, out excelRowInfo))
                    {
                        // Row style:
                        string recommendedActionCode = excelRowInfo.RecommendedActionCode;
                        if (!string.IsNullOrEmpty(recommendedActionCode))
                        {
                            IStyle styleToApply = null;
                            switch (recommendedActionCode)
                            {
                            case "REMOVE":
                                styleToApply = style_Remove;
                                break;

                            case "EASYWORKAROUND":
                                styleToApply = style_EasyWorkaround;
                                break;

                            case "REQUIRESIMPLEMENTATION":
                                styleToApply = style_RequiresImplementation;
                                break;

                            case "NONTRIVIALWORKAROUND":
                                styleToApply = style_NonTrivialWorkaround;
                                break;

                            default:
                                break;
                            }
                            if (styleToApply != null)
                            {
                                worksheet.Range["A" + currentRow.ToString() + ":" + "G" + currentRow.ToString()].CellStyle = styleToApply;
                            }
                            ;
                        }

                        // Recommended action:
                        string recommendedAction = excelRowInfo.RecommendedAction;
                        if (!string.IsNullOrEmpty(recommendedAction))
                        {
                            worksheet.Range["E" + currentRow.ToString()].Text = recommendedAction;
                        }

                        // Estimation of workload:
                        string estimation = excelRowInfo.Estimation;
                        if (!string.IsNullOrEmpty(estimation))
                        {
                            worksheet.Range["F" + currentRow.ToString()].Number = double.Parse(estimation);
                        }

                        // Optional title:
                        string optionalTitle = excelRowInfo.OptionalTitle;
                        if (!string.IsNullOrEmpty(optionalTitle))
                        {
                            featureNameCell.Text = optionalTitle + ": " + featureName;
                        }
                    }

                    // Feature name font size:
                    if (featureName.Length > 2700)
                    {
                        featureNameCell.CellStyle.Font.Size = 7;
                    }
                    else if (featureName.Length > 2000)
                    {
                        featureNameCell.CellStyle.Font.Size = 8;
                    }
                    else if (featureName.Length > 1300)
                    {
                        featureNameCell.CellStyle.Font.Size = 9;
                    }
                    else if (featureName.Length > 600)
                    {
                        featureNameCell.CellStyle.Font.Size = 10;
                    }
                    else
                    {
                        featureNameCell.CellStyle.Font.Size = 11;
                    }

                    // Locations font size:
                    if (sortLocationsJoined.Length > 2700)
                    {
                        locationCell.CellStyle.Font.Size = 7;
                    }
                    else if (sortLocationsJoined.Length > 2000)
                    {
                        locationCell.CellStyle.Font.Size = 8;
                    }
                    else if (sortLocationsJoined.Length > 1300)
                    {
                        locationCell.CellStyle.Font.Size = 9;
                    }
                    else if (sortLocationsJoined.Length > 600)
                    {
                        locationCell.CellStyle.Font.Size = 10;
                    }
                    else
                    {
                        locationCell.CellStyle.Font.Size = 11;
                    }

                    // Increase the row index:
                    ++currentRow;
                }

                // Insert a blank row:
                ++currentRow;
            }

            // Set "Wrap Text" on the columns 2, 3, and 5 (note: columns here are one-based):
            worksheet.Columns[2].WrapText = true;
            worksheet.Columns[3].WrapText = true;
            try
            {
                worksheet.Columns[4].WrapText = true;
                worksheet.Columns[5].WrapText = true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }

            // Vertical align the content of the cells:
            worksheet.Columns[2].VerticalAlignment = ExcelVAlign.VAlignTop;
            worksheet.Columns[3].VerticalAlignment = ExcelVAlign.VAlignTop;
            try
            {
                worksheet.Columns[4].VerticalAlignment = ExcelVAlign.VAlignTop;
                worksheet.Columns[5].VerticalAlignment = ExcelVAlign.VAlignTop;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }

            // Add information at the end about the DLLs that have been analized:
            currentRow += 5;
            string analyzedDllsText = "(Analyzed DLLs: " + string.Join(", ", analyzedDlls) + ")";

            worksheet.Range["A" + currentRow.ToString()].Text = analyzedDllsText;

            // Saving the workbook to disk in XLSX format
            workbook.SaveAs(outputExcelFilePath);
        }
Ejemplo n.º 2
0
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            string coreAssemblyPath      = CoreAssemblyPathTextBox.Text;
            string supportedElementsPath = SupportedElementsPathTextBox.Text;
            string mscorlibFolderPath    = MscorlibFolderPathTextBox.Text;
            string sdkFolderPath         = SDKFolderPathTextBox.Text;
            string otherFoldersPath      = OtherFoldersPathTextBox.Text;

            HashSet <string> xamlFilesToIgnore = null;

            try
            {
                xamlFilesToIgnore = new HashSet <string>(XamlFilesToIgnoreTextBox.Text.Split(',').Select(s => s.Trim().ToLower()));
            }
            catch (Exception ex)
            {
                MessageBox.Show("Invalid list of XAML files to exclude." + Environment.NewLine + Environment.NewLine + ex.ToString());
                return;
            }

            if (string.IsNullOrEmpty(coreAssemblyPath) ||
                string.IsNullOrEmpty(supportedElementsPath) ||
                string.IsNullOrEmpty(mscorlibFolderPath))
            {
                MessageBox.Show("Please fill all the fields before continuing.");
                return;
            }

            if (!File.Exists(supportedElementsPath))
            {
                MessageBox.Show("File not found: " + supportedElementsPath);
                return;
            }

            var featuresAndEstimationsFileProcessor = new FeaturesAndEstimationsFileProcessor(FeaturesAndEstimationsPathTextBox.Text);

            // Save the content of the text boxes for reuse when relaunching the application:
            SaveContentOfTextBoxes();

#if ASK_USER_TO_CHOOSE_ASSEMBLIES_TO_ANALYZE
            // Create OpenFileDialog
            Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();

            // Set filter for file extension and default file extension
            dlg.DefaultExt  = ".dll";
            dlg.Filter      = "Assemblies and executables (*.dll,*.exe)|*.dll;*.exe";
            dlg.Multiselect = true;

            // Display OpenFileDialog by calling ShowDialog method
            Nullable <bool> result = dlg.ShowDialog();

            if (result == true)
            {
                string[] fileNames = dlg.FileNames;
#else
            string[] fileNames = Configuration.AssembliesToAnalyze;

            if (true)
            {
#endif

                if (_coreSupportedMethods == null)
                {
                    _coreSupportedMethods = new CoreSupportedMethodsContainer(coreAssemblyPath);
                }

                var logger = new LoggerThatAggregatesAllErrors();
                var watch  = Stopwatch.StartNew();
                var listOfUnsupportedMethods = new List <UnsupportedMethodInfo>();

                // Get the path of the folder where the first assembly is located, and add that path to the list of places where to look in for resolving referenced assemblies:
                string firstFileToAnalyze = fileNames.FirstOrDefault();
                string additionalFolderWhereToResolveAssemblies = null;
                if (firstFileToAnalyze != null)
                {
                    additionalFolderWhereToResolveAssemblies = System.IO.Path.GetDirectoryName(firstFileToAnalyze);
                }

                // Do the analysis:
                try
                {
                    foreach (var filename in fileNames)
                    {
                        CompatibilityAnalyzer.Analyze(
                            filename,
                            logger,
                            listOfUnsupportedMethods,
                            _coreSupportedMethods,
                            fileNames,
                            Configuration.UrlNamespacesThatBelongToUserCode,
                            Configuration.AttributesToIgnoreInXamlBecauseTheyAreFromBaseClasses,
                            xamlFilesToIgnore,
                            supportedElementsPath,
                            mscorlibFolderPath,
                            sdkFolderPath,
                            otherFoldersPath,
                            skipTypesWhereNoMethodIsActuallyCalled: true,
                            additionalFolderWhereToResolveAssemblies: additionalFolderWhereToResolveAssemblies);;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }

                // Replace all telerik assembly names with "http://schemas.telerik.com/2008/xaml/presentation" so as to avoid differences between namespaces from .xaml files and namespaces from .cs files:
                foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods)
                {
                    if (unsupportedMethodInfo.MethodAssemblyName.StartsWith("Telerik."))
                    {
                        unsupportedMethodInfo.MethodAssemblyName = "http://schemas.telerik.com/2008/xaml/presentation";
                    }
                }

                // Replace all Expression Blend-related namespaces with "http://schemas.microsoft.com/expression/2010/..." so as to avoid differences between namespaces from .xaml files and namespaces from .cs files:
                foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods)
                {
                    if (unsupportedMethodInfo.MethodAssemblyName.StartsWith("Microsoft.Expression.") ||
                        unsupportedMethodInfo.MethodAssemblyName.StartsWith("System.Windows.Interactivity") ||
                        unsupportedMethodInfo.MethodAssemblyName == "http://schemas.microsoft.com/expression/2010/drawing" ||
                        unsupportedMethodInfo.MethodAssemblyName == "http://schemas.microsoft.com/expression/2010/interactivity" ||
                        unsupportedMethodInfo.MethodAssemblyName == "http://schemas.microsoft.com/expression/2010/interactions"
                        )
                    {
                        unsupportedMethodInfo.MethodAssemblyName = "http://schemas.microsoft.com/expression/2010/...";
                    }
                }

                // Aggregate all MEF-related namespaces:
                foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods)
                {
                    if (unsupportedMethodInfo.MethodAssemblyName.StartsWith("System.ComponentModel.Composition."))
                    {
                        unsupportedMethodInfo.MethodAssemblyName = "System.ComponentModel.Composition";
                    }
                }

                // Aggregate all DomainServices-related namespaces:
                foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods)
                {
                    if (unsupportedMethodInfo.MethodAssemblyName.StartsWith("System.ServiceModel.DomainServices.Client."))
                    {
                        unsupportedMethodInfo.MethodAssemblyName = "System.ServiceModel.DomainServices.Client";
                    }
                }

                // Replace all the native MS UI-related assembly names with "http://schemas.microsoft.com/winfx/2006/xaml/presentation" so as to avoid differences between namespaces from .xaml files and namespaces from .cs files:
                foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods)
                {
                    if (unsupportedMethodInfo.MethodAssemblyName == "System.Windows" ||
                        unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Controls" ||
                        unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Controls.Data" ||
                        unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Browser" ||
                        unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Controls.Navigation" ||
                        unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Controls.Toolkit")
                    {
                        unsupportedMethodInfo.MethodAssemblyName = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
                    }
                }

                // Process the list of unsupported methods:
                SortedDictionary <Tuple <string, string>, HashSet <string> > unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed;
                ProcessUnsupportedMethods(listOfUnsupportedMethods, out unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed, rectifyMethodName: true);

                // Remove some stuff:
                foreach (Tuple <string, string> additionalEntryToRemove in Configuration.AdditionalEntriesToRemoveBeforeAggregation)
                {
                    unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Remove(additionalEntryToRemove);
                }

                // For classes that are not defined at all (ie. missing constructor), aggregate all the methods into the same line:
                List <Tuple <string, string> > entriesToRemoveFromDictionary = new List <Tuple <string, string> >();                                                                // This is here because we cannot remove the keys while iterating in the dictionary, so we do it afterwards.
                List <KeyValuePair <Tuple <string, string>, HashSet <string> > > entriesToAddToDictionary = new List <KeyValuePair <Tuple <string, string>, HashSet <string> > >(); // This is here because we cannot add entries while iterating in the dictionary, so we do it afterwards.
                HashSet <Tuple <string, string> > classesForWhichTheMethodsHaveAlreadyBeenAggregated      = new HashSet <Tuple <string, string> >();
                foreach (KeyValuePair <Tuple <string, string>, HashSet <string> > pair in unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed)
                {
                    string           theFullMethodName = pair.Key.Item1;
                    string           theAssemblyName   = pair.Key.Item2;
                    HashSet <string> whereItIsUsed     = pair.Value;

                    if (!Configuration.EntriesToNotAggregateWithOtherEntries.Contains(theFullMethodName))
                    {
#if ALWAYS_AGGREGATE_METHODS
                        if (theFullMethodName.Contains("."))
#else
                        if (theFullMethodName.Contains("..ctor"))
#endif
                        {
#if ALWAYS_AGGREGATE_METHODS
                            string className = theFullMethodName.Substring(0, theFullMethodName.IndexOf("."));
#else
                            string className = theFullMethodName.Substring(0, theFullMethodName.IndexOf("..ctor"));
#endif
                            if (!classesForWhichTheMethodsHaveAlreadyBeenAggregated.Contains(new Tuple <string, string>(className, theAssemblyName)))
                            {
                                classesForWhichTheMethodsHaveAlreadyBeenAggregated.Add(new Tuple <string, string>(className, theAssemblyName));
                                List <string> methodNames = new List <string>();

                                // Find all other methods that concern the same class:
                                foreach (KeyValuePair <Tuple <string, string>, HashSet <string> > pair2 in unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed)
                                {
                                    //todo-perf: optimize performance by creating a dictionary beforehand instead of nesting two iterations!

                                    string           theFullMethodName2 = pair2.Key.Item1;
                                    string           theAssemblyName2   = pair2.Key.Item2;
                                    HashSet <string> whereItIsUsed2     = pair.Value;
                                    if (!Configuration.EntriesToNotAggregateWithOtherEntries.Contains(theFullMethodName2))
                                    {
                                        if (theFullMethodName2.Contains('.') &&
                                            !theFullMethodName2.Contains("..ctor")
                                            )
                                        {
                                            string className2 = theFullMethodName2.Substring(0, theFullMethodName2.IndexOf('.'));
                                            if (className2 == className && theAssemblyName2 == theAssemblyName)
                                            {
                                                string methodName2 = theFullMethodName2.Substring(theFullMethodName2.IndexOf('.') + 1);
                                                methodNames.Add(methodName2);
                                                HashSetHelpers.AddItemsFromOneHashSetToAnother(whereItIsUsed2, whereItIsUsed);
                                                entriesToRemoveFromDictionary.Add(pair2.Key);
                                            }
                                        }
                                    }
                                }

                                // Aggregate them:
                                string newMethodName;
                                if (methodNames.Count > 0)
                                {
                                    if (theFullMethodName.Contains("..ctor"))
                                    {
                                        newMethodName = className + " (members used: " + string.Join(", ", methodNames) + ")";
                                    }
                                    else
                                    {
                                        newMethodName = className + "." + string.Join(", .", methodNames);
                                    }
                                }
                                else
                                {
                                    newMethodName = className;
                                }

                                entriesToRemoveFromDictionary.Add(pair.Key);
                                entriesToAddToDictionary.Add(new KeyValuePair <Tuple <string, string>, HashSet <string> >(
                                                                 new Tuple <string, string>(newMethodName, theAssemblyName),
                                                                 whereItIsUsed));
                            }
                        }
                    }
                }
                // Perform the actual removal and addition (note: this cannot be done before because it is not possible to remove or add an item from a collection while iterating it with "foreach");
                foreach (var key in entriesToRemoveFromDictionary)
                {
                    unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Remove(key);
                }
                foreach (var entry in entriesToAddToDictionary)
                {
                    unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Add(entry.Key, entry.Value);
                }

                //-------------------------------------
                // Merge classes that are related to each other:
                //-------------------------------------
                MergingRelatedClasses.MergeRelatedClasses(Configuration.ClassesRelatedToEachOther, unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed);

                //-------------------------------------
                // Remove elements with an empty assembly name because they are due to "clr-namespace:..." without any assembly being specified, which means that the assembly is the user assembly itself, so we should remove the entry:
                //-------------------------------------
                entriesToRemoveFromDictionary = new List <Tuple <string, string> >();
                foreach (var entry in unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed)
                {
                    if (entry.Key.Item2 == "")
                    {
                        entriesToRemoveFromDictionary.Add(entry.Key);
                    }
                }
                foreach (var key in entriesToRemoveFromDictionary)
                {
                    unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Remove(key);
                }

                //-------------------------------------
                // Remove some additional stuff:
                //-------------------------------------

                foreach (Tuple <string, string> additionalEntryToRemove in Configuration.AdditionalEntriesToRemoveAfterAggregation)
                {
                    unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Remove(additionalEntryToRemove);
                }

                //-------------------------------------
                // Save the result:
                //-------------------------------------

                // Save as Excel document:
                ExcelGenerator.Generate(
                    OutputExcelFilePathTextBox.Text,
                    featuresAndEstimationsFileProcessor,
                    unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed,
                    fileNames.Select(fileName => System.IO.Path.GetFileName(fileName)));

#if SAVE_CSV_DOCUMENT
                // Save as CSV document:
                CsvGenerator.Generate(OutputCsvFilePathTextBox.Text, unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed);
#endif

                var elapsedMs = watch.ElapsedMilliseconds;
                MessageBox.Show(string.Format("Operation completed in {0} seconds.", Math.Floor(elapsedMs / 1000d).ToString()));

                //-------------------------------------
                // Open the result:
                //-------------------------------------

                Process.Start(OutputExcelFilePathTextBox.Text);

                //-------------------------------------
                // Close this app:
                //-------------------------------------

                System.Windows.Application.Current.Shutdown();
            }
        }