// Group: Functions // __________________________________________________________________________ /* Function: HTMLBuildState * * Constructor. * * If createEmptyObjects is set things like <sourceFilesToRebuild> will have objects associated with them. If it's falso, * they will be set to null. Only set it to false if you're going to be creating the objects yourself since they shouldn't be * set to null. */ public HTMLBuildState(bool createEmptyObjects = true) { if (createEmptyObjects) { sourceFilesToRebuild = new IDObjects.NumberSet(); sourceFilesWithContent = new IDObjects.NumberSet(); classFilesToRebuild = new IDObjects.NumberSet(); classFilesWithContent = new IDObjects.NumberSet(); prefixesToRebuild = new StringSet(); foldersToCheckForDeletion = new StringSet(Config.Manager.KeySettingsForPaths); usedMenuDataFiles = new StringTable <NumberSet>(); } else { sourceFilesToRebuild = null; sourceFilesWithContent = null; classFilesToRebuild = null; classFilesWithContent = null; prefixesToRebuild = null; foldersToCheckForDeletion = null; usedMenuDataFiles = null; } needToBuildFramePage = false; needToBuildMainStyleFiles = false; needToBuildMenu = false; needToBuildPrefixIndex = false; }
public Manager(Engine.Instance engineInstance) : base(engineInstance) { linksToResolve = new IDObjects.NumberSet(); newTopicIDsByEndingSymbol = new SafeDictionary <Symbols.EndingSymbol, IDObjects.NumberSet>(); beforeFirstResolve = true; }
/* Function: GetInfoOnLinksThatResolveToTopicID * * Returns aggregate information on all links that resolve to the passed topic ID. * * Parameters: * * topicID - The topic ID to look up links for. * fileIDs - The file IDs of all the links. Will be null if none. * classIDs - The class IDs of all the links. Will be null if none. * */ public void GetInfoOnLinksThatResolveToTopicID(int topicID, out IDObjects.NumberSet fileIDs, out IDObjects.NumberSet classIDs) { accessor.RequireAtLeast(Accessor.LockType.ReadOnly); fileIDs = null; classIDs = null; using (SQLite.Query query = accessor.Connection.Query("SELECT FileID, ClassID FROM Links WHERE TargetTopicID=?", topicID)) { while (query.Step()) { if (fileIDs == null) { fileIDs = new NumberSet(); } fileIDs.Add(query.IntColumn(0)); int classID = query.IntColumn(1); if (classID != 0) { if (classIDs == null) { classIDs = new NumberSet(); } classIDs.Add(classID); } } } }
public void OnDeleteTopic(Topic topic, CodeDB.EventAccessor eventAccessor) { IDObjects.NumberSet parentClassIDs = null; IDObjects.NumberSet parentClassFileIDs = null; if (topic.DefinesClass) { eventAccessor.GetInfoOnClassParents(topic.ClassID, out parentClassIDs, out parentClassFileIDs); } lock (accessLock) { buildState.SourceFilesToRebuild.Add(topic.FileID); if (topic.ClassID != 0) { buildState.ClassFilesToRebuild.Add(topic.ClassID); } if (parentClassIDs != null) { buildState.ClassFilesToRebuild.Add(parentClassIDs); } if (parentClassFileIDs != null) { buildState.SourceFilesToRebuild.Add(parentClassFileIDs); } } }
/* Function: GetChildList */ protected List <Topic> GetChildList() { // First find all the class parent links that resolve to this one and collect the class IDs. IDObjects.NumberSet childClassIDs = null; if (links != null) { foreach (var link in links) { if (link.Type == LinkType.ClassParent && link.TargetClassID == topic.ClassID) { if (childClassIDs == null) { childClassIDs = new IDObjects.NumberSet(); } childClassIDs.Add(link.ClassID); } } } if (childClassIDs == null) { return(null); } // Now find the topics that define those classes. List <Topic> childTopics = new List <Topic>(); foreach (var linkTarget in linkTargets) { if (linkTarget.DefinesClass && childClassIDs.Contains(linkTarget.ClassID)) { childTopics.Add(linkTarget); childClassIDs.Remove(linkTarget.ClassID); } } if (childTopics.Count == 0) { return(null); } // Now sort the child topics by symbol. bool caseSensitive = EngineInstance.Languages.FromID(topic.LanguageID).CaseSensitive; childTopics.Sort( delegate(Topic a, Topic b) { return(a.Symbol.CompareTo(b.Symbol, !caseSensitive)); } ); return(childTopics); }
/* Function: PickNewImageFiles * Returns the IDs for a batch of new image files and their shared lowercase file name, or false if there aren't any. This allows you to * process new image files that could potentially serve as better definitions to existing links. */ public bool PickNewImageFiles(out IDObjects.NumberSet imageFileIDs, out string lcFileName) { lock (accessLock) { if (newImageFileIDsByLCFileName.Count == 0) { imageFileIDs = null; lcFileName = null; return(false); } else { // Once links might be resolved we can't allow this optimization anymore. See the variable's documentation for // the explanation. allLinksAreNew = false; var enumerator = newImageFileIDsByLCFileName.GetEnumerator(); enumerator.MoveNext(); // It's not positioned on the first element by default. lcFileName = enumerator.Current.Key; imageFileIDs = enumerator.Current.Value; newImageFileIDsByLCFileName.Remove(lcFileName); return(true); } } }
/* Function: DeleteTopic */ public void DeleteTopic(Topic topic, IDObjects.NumberSet linksAffected) { lock (accessLock) { // Reresolve affected links linksToResolve.Add(linksAffected); // Remove topic from newTopicIDsByEndingSymbol var endingSymbol = topic.Symbol.EndingSymbol; var newTopicIDs = newTopicIDsByEndingSymbol[endingSymbol]; if (newTopicIDs != null) { newTopicIDs.Remove(topic.TopicID); if (newTopicIDs.IsEmpty) { newTopicIDsByEndingSymbol.Remove(endingSymbol); } } } }
// Group: CodeDB.IChangeWatcher Functions // __________________________________________________________________________ public void OnAddTopic(Topic topic, CodeDB.EventAccessor eventAccessor) { // If this topic defines a class, it's possible it's now the best definition and thus we have to update the class prototypes // of all its parents. We don't have to worry about updating children because that will be taken care of by OnLinkChange // since there would be a class parent link pointing to it. IDObjects.NumberSet parentClassIDs = null; IDObjects.NumberSet parentClassFileIDs = null; if (topic.DefinesClass) { eventAccessor.GetInfoOnClassParents(topic.ClassID, out parentClassIDs, out parentClassFileIDs); } lock (accessLock) { buildState.SourceFilesToRebuild.Add(topic.FileID); if (topic.ClassID != 0) { buildState.ClassFilesToRebuild.Add(topic.ClassID); } if (parentClassIDs != null) { buildState.ClassFilesToRebuild.Add(parentClassIDs); } if (parentClassFileIDs != null) { buildState.SourceFilesToRebuild.Add(parentClassFileIDs); } } }
public void OnDeleteFile(File file) { if (file.Type == FileType.Image) { // We need to get the image links affected by this CodeDB.Accessor accessor = EngineInstance.CodeDB.GetAccessor(); try { accessor.GetReadOnlyLock(); IDObjects.NumberSet linksAffected = accessor.GetImageLinkIDsByTarget(file.ID, Delegates.NeverCancel); accessor.ReleaseLock(); unprocessedChanges.DeleteImageFile((ImageFile)file, linksAffected); } finally { if (accessor.HasLock) { accessor.ReleaseLock(); } accessor.Dispose(); } } }
public void OnDeleteTopic(Topic topic, IDObjects.NumberSet linksAffected, CodeDB.EventAccessor eventAccessor) { // We'll wait for OnChangeLinkTarget to handle linksAffected IDObjects.NumberSet parentClassIDs = null; IDObjects.NumberSet parentClassFileIDs = null; if (topic.DefinesClass) { eventAccessor.GetInfoOnClassParents(topic.ClassID, out parentClassIDs, out parentClassFileIDs); } lock (accessLock) { buildState.SourceFilesToRebuild.Add(topic.FileID); if (topic.ClassID != 0) { buildState.ClassFilesToRebuild.Add(topic.ClassID); } if (parentClassIDs != null) { buildState.ClassFilesToRebuild.Add(parentClassIDs); } if (parentClassFileIDs != null) { buildState.SourceFilesToRebuild.Add(parentClassFileIDs); } } }
/* Function: PickNewTopics * Returns the IDs for a batch of new topics and their shared ending symbol, or false if there aren't any. This allows you to process * new topics that could potentially serve as better definitions to existing links. */ public bool PickNewTopics(out IDObjects.NumberSet topicIDs, out EndingSymbol endingSymbol) { lock (accessLock) { if (newTopicIDsByEndingSymbol.Count == 0) { topicIDs = null; endingSymbol = default(EndingSymbol); return(false); } else { // Once links might be resolved we can't allow this optimization anymore. See the variable's documentation for // the explanation. ignoreNewTopics = false; var enumerator = newTopicIDsByEndingSymbol.GetEnumerator(); enumerator.MoveNext(); // It's not positioned on the first element by default. endingSymbol = enumerator.Current.Key; topicIDs = enumerator.Current.Value; newTopicIDsByEndingSymbol.Remove(endingSymbol); return(true); } } }
// Group: Functions // __________________________________________________________________________ /* Function: UnprocessedChanges */ public UnprocessedChanges() { newOrChangedFileIDs = new IDObjects.NumberSet(); deletedFileIDs = new IDObjects.NumberSet(); accessLock = new object(); }
// Group: Misc Functions // __________________________________________________________________________ /* Function: Cleanup * Cleans up the module's internal data when everything is up to date. This will remove any successfully processed * deleted files, after which they can no longer be found with <FromID()> and their IDs may be reassigned. You can * pass a <CancelDelegate> to interrupt the process if necessary. */ public void Cleanup(CancelDelegate cancelDelegate) { lock (accessLock) { IDObjects.NumberSet toDelete = new IDObjects.NumberSet(); foreach (File file in files) { if (cancelDelegate()) { return; } if (file.Status == FileFlags.Deleted) { toDelete.Add(file.ID); } } foreach (int id in toDelete) { if (cancelDelegate()) { return; } files.Remove(id); } } }
// Group: Functions // __________________________________________________________________________ /* Function: Manager * * Creates a new IDObject manager. * * Parameters: * * keySettings - The <KeySettings> that should apply when referencing objects by name. * * sparse - If false, it assumes the manager will be handling objects with low and mostly consecutive ID numbers. * This allows it to store them in an array where the index maps directly to the ID number, which is very * fast. However, if there are going to be large gaps in the IDs stored this will waste a lot of memory. * * If true, it assumes the manager will be handling objects with high and/or non-consecutive ID numbers * with large gaps between the values. This means it will store them in a sorted array and use a binary * search for lookups and insertions. This is slower but more memory efficient. */ public Manager(KeySettings keySettings, bool sparse) { usedIDs = new IDObjects.NumberSet(); objectsByID = new List <IDObjectType>(); objectsByName = new StringTable <IDObjectType>(keySettings); this.sparse = sparse; }
public void OnDeleteTopic(Topic topic, IDObjects.NumberSet linksAffected, CodeDB.EventAccessor eventAccessor) { // We'll wait for OnChangeLinkTarget to handle linksAffected IDObjects.NumberSet parentClassIDs = null; IDObjects.NumberSet parentClassFileIDs = null; if (topic.DefinesClass) { eventAccessor.GetInfoOnClassParents(topic.ClassID, out parentClassIDs, out parentClassFileIDs); } unprocessedChanges.Lock(); try { unprocessedChanges.AddSourceFile(topic.FileID); if (topic.ClassID != 0) { unprocessedChanges.AddClass(topic.ClassID); } if (parentClassIDs != null) { unprocessedChanges.AddClasses(parentClassIDs); } if (parentClassFileIDs != null) { unprocessedChanges.AddSourceFiles(parentClassFileIDs); } } finally { unprocessedChanges.Unlock(); } }
/* Function: DeleteImageFile */ public void DeleteImageFile(ImageFile imageFile, IDObjects.NumberSet linksAffected) { lock (accessLock) { // Reresolve affected links imageLinksToResolve.Add(linksAffected); // Remove topic from newImageFileIDsByLCFileName string lcFileName = imageFile.FileName.NameWithoutPath.ToLower(); var newImageFileIDs = newImageFileIDsByLCFileName[lcFileName]; if (newImageFileIDs != null) { newImageFileIDs.Remove(imageFile.ID); if (newImageFileIDs.IsEmpty) { newImageFileIDsByLCFileName.Remove(lcFileName); } } } }
public void OnDeleteTopic(Topic topic, IDObjects.NumberSet linksAffected, EventAccessor eventAccessor) { Monitor.Enter(linksToResolve); try { linksToResolve.Add(linksAffected); } finally { Monitor.Exit(linksToResolve); } // Check newTopicIDsByEndingSymbol just in case. We don't want to leave any references to a deleted topic. Monitor.Enter(newTopicIDsByEndingSymbol); try { IDObjects.NumberSet newTopicIDs = newTopicIDsByEndingSymbol[topic.Symbol.EndingSymbol]; if (newTopicIDs != null) { newTopicIDs.Remove(topic.TopicID); } } finally { Monitor.Exit(newTopicIDsByEndingSymbol); } }
// Group: CodeDB.IChangeWatcher Functions // __________________________________________________________________________ public void OnAddTopic(Topic topic, EventAccessor eventAccessor) { Monitor.Enter(newTopicIDsByEndingSymbol); try { if (beforeFirstResolve && EngineInstance.Config.ReparseEverything) { // We don't need to track newTopicIDsByEndingSymbol in this case because the entire output will // be reparsed and every link will be re-added. Thus every link will be on linksToResolve and we don't // need to worry about new topics causing existing links to need to be reresolved. } else { IDObjects.NumberSet newTopicIDs = newTopicIDsByEndingSymbol[topic.Symbol.EndingSymbol]; if (newTopicIDs == null) { newTopicIDs = new IDObjects.NumberSet(); newTopicIDsByEndingSymbol.Add(topic.Symbol.EndingSymbol, newTopicIDs); } newTopicIDs.Add(topic.TopicID); } } finally { Monitor.Exit(newTopicIDsByEndingSymbol); } }
/* Function: FinalizeNewImageFiles * Finalizes processing of a set of new image files and their shared lowercase file name. */ protected void FinalizeNewImageFiles(IDObjects.NumberSet imageFileIDs, string lcFileName) { lock (accessLock) { // DEPENDENCY: Make sure all changes to changesBeingProcessed match the system used in UnprocessedChanges.Count changesBeingProcessed -= imageFileIDs.Count; } }
/* Function: FinalizeNewTopics * Finalizes processing of a set of new topics and their shared ending symbol. */ protected void FinalizeNewTopics(IDObjects.NumberSet topicIDs, EndingSymbol endingSymbol) { lock (accessLock) { // DEPENDENCY: Make sure all changes to changesBeingProcessed match the system used in UnprocessedChanges.Count changesBeingProcessed -= topicIDs.Count; } }
public void AddChangedFiles(IDObjects.NumberSet fileIDs) { lock (accessLock) { deletedFileIDs.Remove(fileIDs); newOrChangedFileIDs.Add(fileIDs); } }
// Group: Initialization and Configuration Functions // __________________________________________________________________________ /* Function: Processor */ public Processor(Engine.Instance engineInstance) : base(engineInstance) { unprocessedChangedFileIDs = new IDObjects.NumberSet(); unprocessedDeletedFileIDs = new IDObjects.NumberSet(); claimedFileIDs = new IDObjects.NumberSet(); accessLock = new object(); }
// Group: Functions // __________________________________________________________________________ /* Function: UnprocessedChanges */ public UnprocessedChanges(bool reparsingEverything = false) { linksToResolve = new IDObjects.NumberSet(); newTopicIDsByEndingSymbol = new SafeDictionary <Symbols.EndingSymbol, IDObjects.NumberSet>(); // If we're reparsing everything we can ignore new topics. See the variable's documentation for the explanation. ignoreNewTopics = reparsingEverything; accessLock = new object(); }
/* Function: Add * Adds a new value to the table. */ public void Add(ObjectType key, int value) { IDObjects.NumberSet numberSet = this[key]; if (numberSet == null) { numberSet = new IDObjects.NumberSet(); this[key] = numberSet; } numberSet.Add(value); }
// Group: Initialization and Configuration Functions // __________________________________________________________________________ /* Function: Manager */ public Manager(Engine.Instance engineInstance) : base(engineInstance) { fileSources = new List <FileSource>(); filters = new List <Filter>(); files = new IDObjects.Manager <File>(Config.Manager.KeySettingsForPaths, false); filesAddedSinceStart = new IDObjects.NumberSet(); unprocessedChanges = new UnprocessedChanges(); accessLock = new object(); changeWatchers = new List <IChangeWatcher>(); }
public void OnAddLink(Link link, CodeDB.EventAccessor eventAccessor) { // If a class parent link was added we have to rebuild all source files that define that class as the class prototype // may have changed. IDObjects.NumberSet filesThatDefineClass = null; if (link.Type == LinkType.ClassParent) { filesThatDefineClass = eventAccessor.GetFileIDsThatDefineClassID(link.ClassID); // If it's resolved we have to rebuild all source files that define the target as well so its children get updated. if (link.TargetClassID != 0) { IDObjects.NumberSet filesThatDefineTarget = eventAccessor.GetFileIDsThatDefineClassID(link.TargetClassID); if (filesThatDefineClass == null) { filesThatDefineClass = filesThatDefineTarget; } else if (filesThatDefineTarget != null) { filesThatDefineClass.Add(filesThatDefineTarget); } } } unprocessedChanges.Lock(); try { // Even if it's not a class parent link we still need to rebuild the source and class files that contain it. We can't // rely on the topic events picking it up because it's possible to change links without changing topics. How? By // changing a using statement, which causes all the links to change. unprocessedChanges.AddSourceFile(link.FileID); if (link.ClassID != 0) { unprocessedChanges.AddClass(link.ClassID); } if (link.Type == LinkType.ClassParent && link.TargetClassID != 0) { unprocessedChanges.AddClass(link.TargetClassID); } if (filesThatDefineClass != null) { unprocessedChanges.AddSourceFiles(filesThatDefineClass); } } finally { unprocessedChanges.Unlock(); } }
/* Function: LowestAvailable * The lowest unused number available for the passed key, starting at one. */ public int LowestAvailable(ObjectType key) { IDObjects.NumberSet numberSet = this[key]; if (numberSet == null) { return(1); } else { return(numberSet.LowestAvailable); } }
/* Function: PickNewImageFiles * Returns the IDs for a batch of new image files and their shared lowercase file name, or false if there aren't any. This allows * you to process new images that could potentially serve as better definitions to existing links. You must pass the values * to <FinalizeNewImageFiles()> after resolving them. */ protected bool PickNewImageFiles(out IDObjects.NumberSet imageFileIDs, out string lcFileName) { lock (accessLock) { if (Manager.UnprocessedChanges.PickNewImageFiles(out imageFileIDs, out lcFileName)) { // DEPENDENCY: Make sure all changes to changesBeingProcessed match the system used in UnprocessedChanges.Count changesBeingProcessed += imageFileIDs.Count; return true; } else { return false; } } }
/* Function: PickNewTopics * Returns the IDs for a batch of new topics and their shared ending symbol, or false if there aren't any. This allows * you to process new topics that could potentially serve as better definitions to existing links. You must pass the values * to <FinalizeNewTopics()> after resolving them. */ protected bool PickNewTopics(out IDObjects.NumberSet topicIDs, out EndingSymbol endingSymbol) { lock (accessLock) { if (Manager.UnprocessedChanges.PickNewTopics(out topicIDs, out endingSymbol)) { // DEPENDENCY: Make sure all changes to changesBeingProcessed match the system used in UnprocessedChanges.Count changesBeingProcessed += topicIDs.Count; return true; } else { return false; } } }
// Group: Functions // __________________________________________________________________________ /* Function: Manager */ public Manager(Engine.Instance engineInstance) : base(engineInstance) { connection = null; databaseLock = new Lock(); usedTopicIDs = new IDObjects.NumberSet(); usedLinkIDs = new IDObjects.NumberSet(); usedClassIDs = new IDObjects.NumberSet(); usedContextIDs = new IDObjects.NumberSet(); classIDReferenceChangeCache = new ReferenceChangeCache(); contextIDReferenceChangeCache = new ReferenceChangeCache(); changeWatchers = new List <IChangeWatcher>(); }