/// <summary>
        /// Calculates which packages and dependencies are dependent on other dependencies and if each dependency that is selected for install is enabled for installation
        /// </summary>
        /// <param name="dependencies">The list of dependencies</param>
        /// <param name="parsedCategoryList">The list of Categories</param>
        /// <param name="suppressSomeLogging">Flag for it some of the more verbose logging should be suppressed</param>
        /// <returns>A list of calculated dependencies to install</returns>
        public static List <Dependency> CalculateDependencies(List <Dependency> dependencies, List <Category> parsedCategoryList, bool suppressSomeLogging, bool showDependencyCalculationErrorMessages)
        {
            //flat list is packages
            List <SelectablePackage> flatListSelect = GetFlatSelectablePackageList(parsedCategoryList);

            //1- build the list of calling mods that need it
            List <Dependency> dependenciesToInstall = new List <Dependency>();

            //create list to track all database dependency references
            List <LogicTracking> refrencedDependencies = new List <LogicTracking>();

            Logging.Debug("Starting step 1 of 4 in dependency calculation: adding from categories");
            foreach (Category category in parsedCategoryList)
            {
                foreach (DatabaseLogic logic in category.Dependencies)
                {
                    refrencedDependencies.Add(new LogicTracking
                    {
                        DatabaseLogic             = logic,
                        ComponentWithDependencies = category
                    });
                    foreach (Dependency dependency in dependencies)
                    {
                        if (logic.PackageName.Equals(dependency.PackageName))
                        {
                            if (!suppressSomeLogging)
                            {
                                Logging.Debug("Category \"{0}\" logic entry added to dependency \"{1}\" of logic type \"{2}\", NotFlag value of \"{3}\"",
                                              category.Name, dependency.PackageName, logic.Logic, logic.NotFlag);
                            }
                            dependency.DatabasePackageLogic.Add(new DatabaseLogic()
                            {
                                PackageName     = category.Name,
                                WillBeInstalled = category.AnyPackagesChecked(),
                                Logic           = logic.Logic,
                                NotFlag         = logic.NotFlag
                            });

                            //log that the categories dependency reference was linked properly
                            logic.RefrenceLinked = true;
                        }
                    }
                }
            }
            Logging.Debug("Step 1 complete");

            Logging.Debug("Starting step 2 of 4 in dependency calculation: adding from selectable packages that use each dependency");
            foreach (SelectablePackage package in flatListSelect)
            {
                //got though each logic property. if the package called is this dependency, then add it to it's list
                foreach (DatabaseLogic logic in package.Dependencies)
                {
                    refrencedDependencies.Add(new LogicTracking
                    {
                        DatabaseLogic             = logic,
                        ComponentWithDependencies = package
                    });
                    foreach (Dependency dependency in dependencies)
                    {
                        if (logic.PackageName.Equals(dependency.PackageName))
                        {
                            if (!suppressSomeLogging)
                            {
                                Logging.Debug("SelectablePackage \"{0}\" logic entry added to dependency \"{1}\" of logic type \"{2}\", NotFlag value of \"{3}\"",
                                              package.PackageName, dependency.PackageName, logic.Logic, logic.NotFlag);
                            }
                            dependency.DatabasePackageLogic.Add(new DatabaseLogic()
                            {
                                //set PackageName to the selectablepackage package name so later we know where this logic entry came from
                                PackageName     = package.PackageName,
                                WillBeInstalled = package.Checked,
                                Logic           = logic.Logic,
                                NotFlag         = logic.NotFlag
                            });

                            //log that the categories dependency reference was linked properly
                            logic.RefrenceLinked = true;
                        }
                    }
                }
            }
            Logging.Debug("Step 2 complete");


            Logging.Debug("Starting step 3 of 4 in dependency calculation: adding dependencies that use each dependency");
            //for each dependency go through each dependency's package logic and if it's called then add it
            foreach (Dependency processingDependency in dependencies)
            {
                foreach (DatabaseLogic logic in processingDependency.Dependencies)
                {
                    refrencedDependencies.Add(new LogicTracking
                    {
                        DatabaseLogic             = logic,
                        ComponentWithDependencies = processingDependency
                    });
                    foreach (Dependency dependency in dependencies)
                    {
                        if (processingDependency.PackageName.Equals(dependency.PackageName))
                        {
                            continue;
                        }
                        if (logic.PackageName.Equals(dependency.PackageName))
                        {
                            if (!suppressSomeLogging)
                            {
                                Logging.Debug("Dependency \"{0}\" logic entry added to dependency \"{1}\" of logic type \"{2}\", NotFlag value of \"{3}\"",
                                              processingDependency.PackageName, dependency.PackageName, logic.Logic, logic.NotFlag);
                            }
                            dependency.DatabasePackageLogic.Add(new DatabaseLogic()
                            {
                                PackageName = processingDependency.PackageName,
                                //by default, dependences that are dependent on dependencies start as false until proven needed
                                WillBeInstalled = false,
                                Logic           = logic.Logic,
                                NotFlag         = logic.NotFlag
                            });

                            //log that the categories dependency reference was linked properly
                            logic.RefrenceLinked = true;
                        }
                    }
                }
            }
            Logging.Debug("Step 3 complete");

            //3a - check if any dependency references were never matched
            //like if a category references dependency the_dependency_packageName, but that package does not exist
            refrencedDependencies = refrencedDependencies.Where((refrence) => !refrence.DatabaseLogic.RefrenceLinked).ToList();
            Logging.Debug("Broken dependency references count: {0}", refrencedDependencies.Count);
            if (refrencedDependencies.Count > 0)
            {
                Logging.Error("The following packages call references to dependencies that do not exist:");
                foreach (LogicTracking logicTracking in refrencedDependencies)
                {
                    Logging.Error("Package: {0} => broken reference: {1}",
                                  logicTracking.ComponentWithDependencies.ComponentInternalName, logicTracking.DatabaseLogic.PackageName);
                }
            }

            //4 - run calculations IN DEPENDENCY LIST ORDER FROM TOP DOWN
            List <Dependency> notProcessedDependnecies = new List <Dependency>(dependencies);

            Logging.Debug("Starting step 4 of 4 in dependency calculation: calculating dependencies from top down (perspective to list)");
            int calcNumber = 1;

            foreach (Dependency dependency in dependencies)
            {
                //first check if this dependency is referencing a dependency that has not yet been processed
                //if so then note it in the log
                if (!suppressSomeLogging)
                {
                    Logging.Debug(string.Empty);
                }
                if (!suppressSomeLogging)
                {
                    Logging.Debug("Calculating if dependency {0} will be installed, {1} of {2}", dependency.PackageName, calcNumber++, dependencies.Count);
                }

                foreach (DatabaseLogic login in dependency.DatabasePackageLogic)
                {
                    List <Dependency> matches = notProcessedDependnecies.Where(dep => login.PackageName.Equals(dep.PackageName)).ToList();
                    if (matches.Count > 0)
                    {
                        string errorMessage = string.Format("Dependency {0} is referenced by the dependency {1} which has not yet been processed! " +
                                                            "This will lead to logic errors in database calculation! Tip: this dependency ({0}) should be BELOW ({1}) in the" +
                                                            "list of dependencies in the editor. Order matters!", dependency.PackageName, login.PackageName);
                        Logging.Error(errorMessage);
                        //if (ModpackSettings.DatabaseDistroVersion == DatabaseVersions.Test)
                        if (showDependencyCalculationErrorMessages)
                        {
                            MessageBox.Show(errorMessage);
                        }
                    }
                }

                //two types of logics - OR and AND (with NOT flags)
                //each can be calculated separately
                List <DatabaseLogic> localOR    = dependency.DatabasePackageLogic.Where(logic => logic.Logic == Logic.OR).ToList();
                List <DatabaseLogic> logicalAND = dependency.DatabasePackageLogic.Where(logic => logic.Logic == Logic.AND).ToList();

                //debug logging
                if (!suppressSomeLogging)
                {
                    Logging.Debug("Logical OR count: {0}", localOR.Count);
                }
                if (!suppressSomeLogging)
                {
                    Logging.Debug("Logical AND count: {0}", logicalAND.Count);
                }

                //if there are no logical ands, then only do ors, vise versa
                bool ORsPass  = localOR.Count > 0 ? false : true;
                bool ANDSPass = logicalAND.Count > 0 ? false : true;

                //if ors and ands are both true already, then something's broken
                if (ORsPass && ANDSPass)
                {
                    Logging.Warning("Logic ORs and ANDs already pass for dependency package {0} (nothing uses it?)", dependency.PackageName);
                    if (!suppressSomeLogging)
                    {
                        Logging.Debug("Skip calculation logic and remove from not processed list");
                    }

                    //remove it from list of not processed dependencies
                    notProcessedDependnecies.RemoveAt(0);
                    continue;
                }

                //calc the ORs first
                if (!suppressSomeLogging)
                {
                    Logging.Debug("Processing OR logic");
                }
                foreach (DatabaseLogic orLogic in localOR)
                {
                    //OR logic - if any mod/dependency is checked, then it's installed and can stop there
                    //because only one of them needs to be true
                    //same case goes for negatives - if mod is NOT checked and negateFlag
                    if (!orLogic.WillBeInstalled)
                    {
                        if (!suppressSomeLogging)
                        {
                            Logging.Debug("Skipping logic check of package {0} because it is not set for installation!", orLogic.PackageName);
                        }
                        continue;
                    }
                    else
                    {
                        if (!orLogic.NotFlag)
                        {
                            if (!suppressSomeLogging)
                            {
                                Logging.Debug("Package {0}, checked={1}, notFlag={2}, is checked and notFlag is false (package must be checked), sets orLogic to pass!", orLogic.PackageName, orLogic.WillBeInstalled, orLogic.NotFlag);
                            }
                            ORsPass = true;
                            break;
                        }
                        else if (orLogic.NotFlag)
                        {
                            if (!suppressSomeLogging)
                            {
                                Logging.Debug("Package {0}, checked={1}, notFlag={2}, is NOT checked and notFlag is true (package must NOT be checked), sets orLogic to pass!", orLogic.PackageName, orLogic.WillBeInstalled, orLogic.NotFlag);
                            }
                            ORsPass = true;
                            break;
                        }
                        else
                        {
                            if (!suppressSomeLogging)
                            {
                                Logging.Debug("Package {0}, checked={1}, notFlag={2}, does not set orLogic to pass!", orLogic.PackageName, orLogic.WillBeInstalled, orLogic.NotFlag);
                            }
                        }
                    }
                }

                //now calc the ands
                if (!suppressSomeLogging)
                {
                    Logging.Debug("Processing AND logic");
                }
                foreach (DatabaseLogic andLogic in logicalAND)
                {
                    if (andLogic.WillBeInstalled && !andLogic.NotFlag)
                    {
                        if (!suppressSomeLogging)
                        {
                            Logging.Debug("Package {0}, checked={1}, notFlag={2}, is checked and notFlag is false (package must be checked), correct AND logic, continue", andLogic.PackageName, andLogic.WillBeInstalled, andLogic.NotFlag);
                        }
                        ANDSPass = true;
                    }
                    else if (!andLogic.WillBeInstalled && andLogic.NotFlag)
                    {
                        if (!suppressSomeLogging)
                        {
                            Logging.Debug("Package {0}, checked={1}, notFlag={2}, is NOT checked and notFlag is true (package must NOT be checked), correct AND logic, continue", andLogic.PackageName, andLogic.WillBeInstalled, andLogic.NotFlag);
                        }
                        ANDSPass = true;
                    }
                    else
                    {
                        if (!suppressSomeLogging)
                        {
                            Logging.Debug("Package {0}, checked={1}, notFlag={2}, incorrect AND logic, set ANDSPass=false and stop processing!", andLogic.PackageName, andLogic.WillBeInstalled, andLogic.NotFlag);
                        }
                        ANDSPass = false;
                        break;
                    }
                }

                string final = string.Format("Final result for dependency {0}: AND={1}, OR={2}", dependency.PackageName, ANDSPass, ORsPass);
                if (ANDSPass && ORsPass)
                {
                    if (suppressSomeLogging)
                    {
                        Logging.Info(LogOptions.MethodAndClassName, "Dependency {0} WILL be installed!", dependency.PackageName);
                    }
                    else
                    {
                        Logging.Debug("{0} (AND and OR) = TRUE, dependency WILL be installed!", final);
                    }
                    dependenciesToInstall.Add(dependency);
                }
                else
                {
                    if (!suppressSomeLogging)
                    {
                        Logging.Debug("{0} (AND and OR) = FALSE, dependency WILL NOT be installed!", final);
                    }
                }

                if (dependency.DatabasePackageLogic.Count > 0 && (ANDSPass && ORsPass))
                {
                    if (!suppressSomeLogging)
                    {
                        Logging.Debug("Updating future references (like logicalDependnecies) for if dependency was checked");
                    }
                    //update any dependencies that use it
                    foreach (DatabaseLogic callingLogic in dependency.Dependencies)
                    {
                        //get the dependency (if it is a dependency) that called this dependency
                        List <Dependency> found = dependencies.Where(dep => dep.PackageName.Equals(callingLogic.PackageName)).ToList();

                        if (found.Count > 0)
                        {
                            Dependency refrenced = found[0];
                            //now get the logic entry that references the original calculated dependency
                            List <DatabaseLogic> foundLogic = refrenced.DatabasePackageLogic.Where(logic => logic.PackageName.Equals(dependency.PackageName)).ToList();
                            if (foundLogic.Count > 0)
                            {
                                Logging.Debug("Logic reference entry for dependency {0} updated to {1}", refrenced.PackageName, ANDSPass && ORsPass);
                                foundLogic[0].WillBeInstalled = ANDSPass && ORsPass;
                            }
                            else
                            {
                                Logging.Error("Found logics count is 0 for updating references");
                            }
                        }
                        else
                        {
                            Logging.Error("Found count is 0 for updating references");
                        }
                    }
                }

                //remove it from list of not processed dependencies
                notProcessedDependnecies.RemoveAt(0);
            }

            Logging.Debug("Step 4 complete");
            return(dependenciesToInstall);
        }
示例#2
0
        protected override void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    if (Packages != null)
                    {
                        if (Packages.Count > 0)
                        {
                            foreach (SelectablePackage package in Packages)
                            {
                                package.Dispose();
                            }
                        }
                        Packages.Clear();
                        Packages = null;
                    }
                    if (Dependencies != null)
                    {
                        Dependency.ClearLogics(Dependencies);
                        Dependencies = null;
                    }
                    // TODO: dispose managed state (managed objects)
                    if (ParentCategory != null)
                    {
                        ParentCategory = null;
                    }
                    if (Parent != null)
                    {
                        Parent = null;
                    }
                    if (TopParent != null)
                    {
                        TopParent = null;
                    }
                    if (UserFiles != null)
                    {
                        foreach (UserFile file in UserFiles)
                        {
                            file.FilesSaved.Clear();
                        }
                        UserFiles.Clear();
                        UserFiles = null;
                    }
                    if (Medias != null)
                    {
                        foreach (Media media in Medias)
                        {
                            media.SelectablePackageParent = null;
                        }
                        Medias.Clear();
                        Medias = null;
                    }
                    if (UIComponent != null)
                    {
                        UIComponent.Package = null;
                        UIComponent         = null;
                    }
                    if (ParentUIComponent != null)
                    {
                        ParentUIComponent.Package = null;
                        ParentUIComponent         = null;
                    }
                    if (TopParentUIComponent != null)
                    {
                        TopParentUIComponent.Package = null;
                        TopParentUIComponent         = null;
                    }
                    if (RelhaxWPFComboBoxList != null)
                    {
                        for (int i = 0; i < RelhaxWPFComboBoxList.Count(); i++)
                        {
                            if (RelhaxWPFComboBoxList[i] != null)
                            {
                                RelhaxWPFComboBoxList[i] = null;
                            }
                        }
                        RelhaxWPFComboBoxList = null;
                    }
                    if (ChildBorder != null)
                    {
                        ChildBorder = null;
                    }
                    if (ChildStackPanel != null)
                    {
                        ChildStackPanel = null;
                    }
                    if (ParentBorder != null)
                    {
                        ParentBorder = null;
                    }
                    if (ParentStackPanel != null)
                    {
                        ParentStackPanel = null;
                    }
                    if (ContentControl != null)
                    {
                        ContentControl = null;
                    }
                    if (ScrollViewer != null)
                    {
                        ScrollViewer = null;
                    }
                    if (TreeViewItem != null)
                    {
                        TreeViewItem = null;
                    }
                    if (TreeView != null)
                    {
                        TreeView = null;
                    }
                    if (TabIndex != null)
                    {
                        TabIndex = null;
                    }
                }

                // TODO: free unmanaged resources (unmanaged objects) and override finalizer
                // TODO: set large fields to null
                disposedValue = true;
            }

            base.Dispose(disposing);
        }