/// <summary> /// Checks if a Keyworded record contains a specific Keyword, by EditorID. /// Also looks up that keyword in the given cache. /// <br /> /// Aspects: IKeywordedGetter<IKeywordCommonGetter> /// </summary> /// <param name="keyworded">Keyworded record to check</param> /// <param name="editorID">EditorID of the Keyword to look for</param> /// <param name="cache">LinkCache to resolve against</param> /// <param name="keyword">Keyword record retrieved, if keyworded record does contain</param> /// <param name="stringComparison"> /// What string comparison type to use.<br /> /// By default EditorIDs are case insensitive. /// </param> /// <returns>True if the Keyworded record contains a Keyword link that points to a winning Keyword record /w the given EditorID</returns> public static bool TryResolveKeyword <TKeyword>( this IKeywordedGetter <TKeyword> keyworded, string editorID, ILinkCache cache, [MaybeNullWhen(false)] out TKeyword keyword, StringComparison stringComparison = StringComparison.OrdinalIgnoreCase) where TKeyword : class, IKeywordCommonGetter { // ToDo // Consider EDID link cache systems if/when available if (keyworded.Keywords == null) { keyword = default; return(false); } foreach (var keywordForm in keyworded.Keywords.Select(link => link.FormKey)) { if (cache.TryResolve <TKeyword>(keywordForm, out keyword) && (keyword.EditorID?.Equals(editorID, stringComparison) ?? false)) { return(true); } } keyword = default; return(false); }
/// <summary> /// Attempts to locate link winning target record in given Link Cache. /// </summary> /// <param name="link">FormLink to resolve</param> /// <param name="cache">Link Cache to resolve against</param> /// <param name="majorRecord">Located record if successful</param> /// <returns>True if link was resolved and a record was retrieved</returns> /// <typeparam name="TMajor">Major Record type to resolve to</typeparam> public static bool TryResolve <TMajor>(this IFormLinkGetter link, ILinkCache cache, [MaybeNullWhen(false)] out TMajor majorRecord) where TMajor : class, IMajorRecordCommonGetter { if (!link.FormKeyNullable.TryGet(out var formKey)) { majorRecord = default; return(false); } return(cache.TryResolve(formKey, out majorRecord)); }
/// <summary> /// Attempts to locate link's winning target record in given Link Cache. /// </summary> /// <param name="link">Link to resolve</param> /// <param name="cache">Link Cache to resolve against</param> /// <param name="majorRecord">Major Record if located</param> /// <returns>True if successful in linking to record</returns> /// <typeparam name="TMajor">Major Record type to resolve to</typeparam> public static bool TryResolve <TMajor>(this IFormLinkGetter <TMajor> link, ILinkCache cache, [MaybeNullWhen(false)] out TMajor majorRecord) where TMajor : class, IMajorRecordGetter { if (link.FormKeyNullable is not { } formKey) { majorRecord = default; return(false); } return(cache.TryResolve <TMajor>(formKey, out majorRecord)); }
/// <summary> /// Checks if a Keyworded record contains a specific Keyword, by FormKey. /// Also looks up that keyword in the given cache. <br /> /// <br /> /// Note that this function only succeeds if the record contains the keyword, /// and the cache found it as well. <br /> /// <br /> /// It is possible that the record contains the keyword, but it could not be found /// by the cache. /// <br /> /// Aspects: IKeywordedGetter<IKeywordCommonGetter> /// </summary> /// <param name="keyworded">Keyworded record to check</param> /// <param name="keywordKey">FormKey of the Keyword record to look for</param> /// <param name="cache">LinkCache to resolve against</param> /// <param name="keyword">Keyword record retrieved, if keyworded record does contain</param> /// <returns>True if the Keyworded record contains a Keyword link /w the given FormKey</returns> public static bool TryResolveKeyword <TKeyword>( this IKeywordedGetter <TKeyword> keyworded, FormKey keywordKey, ILinkCache cache, [MaybeNullWhen(false)] out TKeyword keyword) where TKeyword : class, IKeywordCommonGetter { if (!keyworded.Keywords?.Any(x => x.FormKey == keywordKey) ?? true) { keyword = default; return(false); } return(cache.TryResolve(keywordKey, out keyword)); }
/// <summary> /// Duplicates records into a given mod 'modToDuplicateInto', which originated from target ModKey 'modKeyToDuplicateFrom'.<br /> /// Only considers records that are currently within the target modToDuplicateInto, which are then duplicated. <br/> /// Records from the modKeyToDuplicateFrom that are not within or referenced by records in the target mod are skipped.<br /> /// <br /> /// End result will be that all records that the given modToDuplicateInto contains or references that originate from the target modKeyToDuplicateFrom will be duplicated in /// and replace the records they duplicated. No references to the modKeyToDuplicateFrom will remain. /// </summary> /// <typeparam name="TMod"></typeparam> /// <typeparam name="TModGetter"></typeparam> /// <param name="modToDuplicateInto">Mod to duplicate into and originate new records from</param> /// <param name="linkCache">LinkCache for lookup</param> /// <param name="modKeyToDuplicateFrom">ModKey to search modToDuplicateInto for, and duplicate records that originate from modKeyToDuplicateFrom</param> /// <param name="mapping">Out parameter to store the resulting duplication mappings that were made</param> /// <param name="typesToInspect"> /// Types to iterate and look at within modToDuplicateInto for references to modKeyToDuplicateFrom<br /> /// Only use if you know specifically the types that can reference modKeyToDuplicateFrom, and want a little bit of speed /// by not checking uninteresting records. /// </param> public static void DuplicateFromOnlyReferenced <TMod, TModGetter>( this TMod modToDuplicateInto, ILinkCache <TMod, TModGetter> linkCache, ModKey modKeyToDuplicateFrom, out Dictionary <FormKey, FormKey> mapping, params Type[] typesToInspect) where TModGetter : class, IModGetter where TMod : class, TModGetter, IMod { if (modKeyToDuplicateFrom == modToDuplicateInto.ModKey) { throw new ArgumentException("Cannot pass the target mod's Key as the one to extract and self contain"); } // Compile list of things to duplicate HashSet <IFormLinkGetter> identifiedLinks = new(); HashSet <FormKey> passedLinks = new(); var implicits = Implicits.Get(modToDuplicateInto.GameRelease); void AddAllLinks(IFormLinkGetter link) { if (link.FormKey.IsNull) { return; } if (!passedLinks.Add(link.FormKey)) { return; } if (implicits.RecordFormKeys.Contains(link.FormKey)) { return; } if (link.FormKey.ModKey == modKeyToDuplicateFrom) { identifiedLinks.Add(link); } if (!linkCache.TryResolve(link.FormKey, link.Type, out var linkRec)) { return; } foreach (var containedLink in linkRec.ContainedFormLinks) { if (containedLink.FormKey.ModKey != modKeyToDuplicateFrom) { continue; } AddAllLinks(containedLink); } } var enumer = typesToInspect == null || typesToInspect.Length == 0 ? modToDuplicateInto.EnumerateMajorRecords() : typesToInspect.SelectMany(x => modToDuplicateInto.EnumerateMajorRecords(x)); foreach (var rec in enumer) { AddAllLinks(new FormLinkInformation(rec.FormKey, rec.Registration.GetterType)); } // Duplicate in the records mapping = new(); foreach (var identifiedRec in identifiedLinks) { if (!linkCache.TryResolveContext(identifiedRec.FormKey, identifiedRec.Type, out var rec)) { throw new KeyNotFoundException($"Could not locate record to make self contained: {identifiedRec}"); } var dup = rec.DuplicateIntoAsNewRecord(modToDuplicateInto, rec.Record.EditorID); mapping[rec.Record.FormKey] = dup.FormKey; // ToDo // Move this out of loop, and remove off a new IEnumerable<IFormLinkGetter> call modToDuplicateInto.Remove(identifiedRec.FormKey, identifiedRec.Type); } // Remap links modToDuplicateInto.RemapLinks(mapping); }