/// <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); }
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); }