diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-08-02 08:21:29 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-08-02 08:21:29 +0000 |
| commit | 61bfe2d70cae6be8c4086a210a5451135ccca9ea (patch) | |
| tree | 62bf7808b1b2bfe5f56fe1e329871fb0991d0687 /docs/tool/Modules/NaturalDocs/SourceDB.pm | |
| parent | a13b94f9e0bca8ea892311d9d9e0c0bc48616ea7 (diff) | |
| download | zcatch-61bfe2d70cae6be8c4086a210a5451135ccca9ea.tar.gz zcatch-61bfe2d70cae6be8c4086a210a5451135ccca9ea.zip | |
added doc tool
Diffstat (limited to 'docs/tool/Modules/NaturalDocs/SourceDB.pm')
| -rw-r--r-- | docs/tool/Modules/NaturalDocs/SourceDB.pm | 678 |
1 files changed, 678 insertions, 0 deletions
diff --git a/docs/tool/Modules/NaturalDocs/SourceDB.pm b/docs/tool/Modules/NaturalDocs/SourceDB.pm new file mode 100644 index 00000000..a1928f95 --- /dev/null +++ b/docs/tool/Modules/NaturalDocs/SourceDB.pm @@ -0,0 +1,678 @@ +############################################################################### +# +# Package: NaturalDocs::SourceDB +# +############################################################################### +# +# SourceDB is an experimental package meant to unify the tracking of various elements in the source code. +# +# Requirements: +# +# - All extension packages must call <RegisterExtension()> before they can be used. +# +# +# Architecture: The Idea +# +# For quite a while Natural Docs only needed <SymbolTable>. However, 1.3 introduced the <ClassHierarchy> package +# which duplicated some of its functionality to track classes and parent references. 1.4 now needs <ImageReferenceTable>, +# so this package was an attempt to isolate the common functionality so the wheel doesn't have to keep being rewritten as +# the scope of Natural Docs expands. +# +# SourceDB is designed around <Extensions> and items. The purposefully vague "items" are anything in the source code +# that we need to track the definitions of. Extensions are the packages to track them, only they're derived from +# <NaturalDocs::SourceDB::Extension> and registered with this package instead of being free standing and duplicating +# functionality such as watched files. +# +# The architecture on this package isn't comprehensive yet. As more extensions are added or previously made free standing +# packages are migrated to it it will expand to encompass them. However, it's still experimental so this concept may +# eventually be abandoned for something better instead. +# +# +# Architecture: Assumptions +# +# SourceDB is built around certain assumptions. +# +# One item per file: +# +# SourceDB assumes that only the first item per file with a particular item string is relevant. For example, if two functions +# have the exact same name, there's no way to link to the second one either in HTML or internally so it doesn't matter for +# our purposes. Likewise, if two references are exactly the same they go to the same target, so it doesn't matter whether +# there's one or two or a thousand. All that matters is that at least one reference exists in this file because you only need +# to determine whether the entire file gets rebuilt. If two items are different in some meaningful way, they should generate +# different item strings. +# +# Watched file parsing: +# +# SourceDB assumes the parse method is that the information that was stored from Natural Docs' previous run is loaded, a +# file is watched, that file is reparsed, and then <AnalyzeWatchedFileChanges()> is called. When the file is reparsed all +# items within it are added the same as if the file was never parsed before. +# +# If there's a new item this time around, that's fine no matter what. However, a changed item wouldn't normally be +# recorded because the previous run's definition is seen as the first one and subsequent ones are ignored. Also, deleted +# items would normally not be recorded either because we're only adding. +# +# The watched file method fixes this because everything is also added to a second, clean database specifically for the +# watched file. Because it starts clean, it always gets the first definition from the current parse which can then be +# compared to the original by <AnalyzeWatchedFileChanges()>. Because it starts clean you can also compare it to the +# main database to see if anything was deleted, because it would appear in the main database but not the watched one. +# +# This means that functions like <ChangeDefinition()> and <DeleteDefinition()> should only be called by +# <AnalyzeWatchedFileChanges()>. Externally only <AddDefinition()> should be called. <DeleteItem()> is okay to be +# called externally because entire items aren't managed by the watched file database, only definitions. +# +# +############################################################################### + +# This file is part of Natural Docs, which is Copyright (C) 2003-2008 Greg Valure +# Natural Docs is licensed under the GPL + +use strict; +use integer; + + +use NaturalDocs::SourceDB::Extension; +use NaturalDocs::SourceDB::Item; +use NaturalDocs::SourceDB::ItemDefinition; +use NaturalDocs::SourceDB::File; +use NaturalDocs::SourceDB::WatchedFileDefinitions; + + +package NaturalDocs::SourceDB; + + +############################################################################### +# Group: Types + + +# +# Type: ExtensionID +# +# A unique identifier for each <NaturalDocs::SourceDB> extension as given out by <RegisterExtension()>. +# + + + +############################################################################### +# Group: Variables + + +# +# array: extensions +# +# An array of <NaturalDocs::SourceDB::Extension>-derived extensions, as added with <RegisterExtension()>. The indexes +# are the <ExtensionIDs> and the values are package references. +# +my @extensions; + +# +# array: extensionUsesDefinitionObjects +# +# An array where the indexes are <ExtensionIDs> and the values are whether that extension uses its own definition class +# derived from <NaturalDocs::SourceDB::ItemDefinition> or it just tracks their existence. +# +my @extensionUsesDefinitionObjects; + + + +# +# array: items +# +# The array of source items. The <ExtensionIDs> are the indexes, and the values are hashrefs mapping the item +# string to <NaturalDocs::SourceDB::Item>-derived objects. Hashrefs may be undef. +# +my @items; + + +# +# hash: files +# +# A hashref mapping source <FileNames> to <NaturalDocs::SourceDB::Files>. +# +my %files; + + +# +# object: watchedFile +# +# When a file is being watched for changes, will be a <NaturalDocs::SourceDB::File> for that file. Is undef otherwise. +# +# When the file is parsed, items are added to both this and the version in <files>. Thus afterwards we can compare the two to +# see if any were deleted since the last time Natural Docs was run, because they would be in the <files> version but not this +# one. +# +my $watchedFile; + + +# +# string: watchedFileName +# +# When a file is being watched for changes, will be the <FileName> of the file being watched. Is undef otherwise. +# +my $watchedFileName; + + +# +# object: watchedFileDefinitions +# +# When a file is being watched for changes, will be a <NaturalDocs::SourceDB::WatchedFileDefinitions> object. Is undef +# otherwise. +# +# When the file is parsed, items are added to both this and the version in <items>. Since only the first definition is kept, this +# will always have the definition info from the file whereas the version in <items> will have the first definition as of the last time +# Natural Docs was run. Thus they can be compared to see if the definitions of items that existed the last time around have +# changed. +# +my $watchedFileDefinitions; + + + +############################################################################### +# Group: Extension Functions + + +# +# Function: RegisterExtension +# +# Registers a <NaturalDocs::SourceDB::Extension>-derived package and returns a unique <ExtensionID> for it. All extensions +# must call this before they can be used. +# +# Registration Order: +# +# The order in which extensions register is important. Whenever possible, items are added in the order their extensions +# registered. However, items are changed and deleted in the reverse order. Take advantage of this to minimize +# churn between extensions that are dependent on each other. +# +# For example, when symbols are added or deleted they may cause references to be retargeted and thus their files need to +# be rebuilt. However, adding or deleting references never causes the symbols' files to be rebuilt. So it makes sense that +# symbols should be created before references, and that references should be deleted before symbols. +# +# Parameters: +# +# extension - The package or object of the extension. Must be derived from <NaturalDocs::SourceDB::Extension>. +# usesDefinitionObjects - Whether the extension uses its own class derived from <NaturalDocs::SourceDB::ItemDefinition> +# or simply tracks each definitions existence. +# +# Returns: +# +# An <ExtensionID> unique to the extension. This should be saved because it's required in functions such as <AddItem()>. +# +sub RegisterExtension #(package extension, bool usesDefinitionObjects) => ExtensionID + { + my ($self, $extension, $usesDefinitionObjects) = @_; + + push @extensions, $extension; + push @extensionUsesDefinitionObjects, $usesDefinitionObjects; + + return scalar @extensions - 1; + }; + + + + +############################################################################### +# Group: File Functions + + +# +# Function: Load +# +# Loads the data of the source database and all the extensions. Will call <NaturalDocs::SourceDB::Extension->Load()> for +# all of them, unless there's a situation where all the source files are going to be reparsed anyway in which case it's not needed. +# +sub Load + { + my $self = shift; + + # No point loading if RebuildData is set. + if (!NaturalDocs::Settings->RebuildData()) + { + # If any load fails, stop loading the rest and just reparse all the source files. + my $success = 1; + + for (my $extension = 0; $extension < scalar @extensions && $success; $extension++) + { + $success = $extensions[$extension]->Load(); + }; + + if (!$success) + { NaturalDocs::Project->ReparseEverything(); }; + }; + }; + + +# +# Function: Save +# +# Saves the data of the source database and all its extensions. Will call <NaturalDocs::SourceDB::Extension->Save()> for all +# of them. +# +sub Save + { + my $self = shift; + + for (my $extension = scalar @extensions - 1; $extension >= 0; $extension--) + { + $extensions[$extension]->Save(); + }; + }; + + +# +# Function: PurgeDeletedSourceFiles +# +# Removes all data associated with deleted source files. +# +sub PurgeDeletedSourceFiles + { + my $self = shift; + + my $filesToPurge = NaturalDocs::Project->FilesToPurge(); + + # Extension is the outermost loop because we want the extensions added last to have their definitions removed first to cause + # the least amount of churn between interdependent extensions. + for (my $extension = scalar @extensions - 1; $extension >= 0; $extension--) + { + foreach my $file (keys %$filesToPurge) + { + if (exists $files{$file}) + { + my @items = $files{$file}->ListItems($extension); + + foreach my $item (@items) + { + $self->DeleteDefinition($extension, $item, $file); + }; + }; # file exists + }; # each file + }; # each extension + }; + + + + + +############################################################################### +# Group: Item Functions + + +# +# Function: AddItem +# +# Adds the passed item to the database. This will not work if the item string already exists. The item added should *not* +# already have definitions attached. Only use this to add blank items and then call <AddDefinition()> instead. +# +# Parameters: +# +# extension - An <ExtensionID>. +# itemString - The string serving as the item identifier. +# item - An object derived from <NaturalDocs::SourceDB::Item>. +# +# Returns: +# +# Whether the item was added, that is, whether it was the first time this item was added. +# +sub AddItem #(ExtensionID extension, string itemString, NaturalDocs::SourceDB::Item item) => bool + { + my ($self, $extension, $itemString, $item) = @_; + + if (!defined $items[$extension]) + { $items[$extension] = { }; }; + + if (!exists $items[$extension]->{$itemString}) + { + if ($item->HasDefinitions()) + { die "Tried to add an item to SourceDB that already had definitions."; }; + + $items[$extension]->{$itemString} = $item; + return 1; + }; + + return 0; + }; + + +# +# Function: GetItem +# +# Returns the <NaturalDocs::SourceDB::Item>-derived object for the passed <ExtensionID> and item string, or undef if there +# is none. +# +sub GetItem #(ExtensionID extension, string itemString) => bool + { + my ($self, $extensionID, $itemString) = @_; + + if (defined $items[$extensionID]) + { return $items[$extensionID]->{$itemString}; } + else + { return undef; }; + }; + + +# +# Function: DeleteItem +# +# Deletes the record of the passed <ExtensionID> and item string. Do *not* delete items that still have definitions. Use +# <DeleteDefinition()> first. +# +# Parameters: +# +# extension - The <ExtensionID>. +# itemString - The item's identifying string. +# +# Returns: +# +# Whether it was successful, meaning whether an entry existed for it. +# +sub DeleteItem #(ExtensionID extension, string itemString) => bool + { + my ($self, $extension, $itemString) = @_; + + if (defined $items[$extension] && exists $items[$extension]->{$itemString}) + { + if ($items[$extension]->{$itemString}->HasDefinitions()) + { die "Tried to delete an item from SourceDB that still has definitions."; }; + + delete $items[$extension]->{$itemString}; + return 1; + } + else + { return 0; }; + }; + + +# +# Function: HasItem +# +# Returns whether there is an item defined for the passed <ExtensionID> and item string. +# +sub HasItem #(ExtensionID extension, string itemString) => bool + { + my ($self, $extension, $itemString) = @_; + + if (defined $items[$extension]) + { return (exists $items[$extension]->{$itemString}); } + else + { return 0; }; + }; + + +# +# Function: GetAllItemsHashRef +# +# Returns a hashref of all the items defined for an extension. *Do not change the contents.* The keys are the item strings and +# the values are <NaturalDocs::SourceDB::Items> or derived classes. +# +sub GetAllItemsHashRef #(ExtensionID extension) => hashref + { + my ($self, $extension) = @_; + return $items[$extension]; + }; + + + +############################################################################### +# Group: Definition Functions + + +# +# Function: AddDefinition +# +# Adds a definition to an item. Assumes the item was already created with <AddItem()>. If there's already a definition for this +# file in the item, the new definition will be ignored. +# +# Parameters: +# +# extension - The <ExtensionID>. +# itemString - The item string. +# file - The <FileName> the definition is in. +# definition - If you're using a custom <NaturalDocs::SourceDB::ItemDefinition> class, you must include an object for it here. +# Otherwise this parameter is ignored. +# +# Returns: +# +# Whether the definition was added, which is to say, whether this was the first definition for the passed <FileName>. +# +sub AddDefinition #(ExtensionID extension, string itemString, FileName file, optional NaturalDocs::SourceDB::ItemDefinition definition) => bool + { + my ($self, $extension, $itemString, $file, $definition) = @_; + + + # Items + + my $item = $self->GetItem($extension, $itemString); + + if (!defined $item) + { die "Tried to add a definition to an undefined item in SourceDB."; }; + + if (!$extensionUsesDefinitionObjects[$extension]) + { $definition = 1; }; + + my $result = $item->AddDefinition($file, $definition); + + + # Files + + if (!exists $files{$file}) + { $files{$file} = NaturalDocs::SourceDB::File->New(); }; + + $files{$file}->AddItem($extension, $itemString); + + + # Watched File + + if ($self->WatchingFileForChanges()) + { + $watchedFile->AddItem($extension, $itemString); + + if ($extensionUsesDefinitionObjects[$extension]) + { $watchedFileDefinitions->AddDefinition($extension, $itemString, $definition); }; + }; + + + return $result; + }; + + +# +# Function: ChangeDefinition +# +# Changes the definition of an item. This function is only used for extensions that use custom +# <NaturalDocs::SourceDB::ItemDefinition>-derived classes. +# +# Parameters: +# +# extension - The <ExtensionID>. +# itemString - The item string. +# file - The <FileName> the definition is in. +# definition - The definition, which must be an object derived from <NaturalDocs::SourceDB::ItemDefinition>. +# +sub ChangeDefinition #(ExtensionID extension, string itemString, FileName file, NaturalDocs::SourceDB::ItemDefinition definition) + { + my ($self, $extension, $itemString, $file, $definition) = @_; + + my $item = $self->GetItem($extension, $itemString); + + if (!defined $item) + { die "Tried to change the definition of an undefined item in SourceDB."; }; + + if (!$extensionUsesDefinitionObjects[$extension]) + { die "Tried to change the definition of an item in an extension that doesn't use definition objects in SourceDB."; }; + + if (!$item->HasDefinition($file)) + { die "Tried to change a definition that doesn't exist in SourceDB."; }; + + $item->ChangeDefinition($file, $definition); + $extensions[$extension]->OnChangedDefinition($itemString, $file); + }; + + +# +# Function: GetDefinition +# +# If the extension uses custom <NaturalDocs::SourceDB::ItemDefinition> classes, returns it for the passed definition or undef +# if it doesn't exist. Otherwise returns whether it exists. +# +sub GetDefinition #(ExtensionID extension, string itemString, FileName file) => NaturalDocs::SourceDB::ItemDefinition or bool + { + my ($self, $extension, $itemString, $file) = @_; + + my $item = $self->GetItem($extension, $itemString); + + if (!defined $item) + { return undef; }; + + return $item->GetDefinition($file); + }; + + +# +# Function: DeleteDefinition +# +# Removes the definition for the passed item. Returns whether it was successful, meaning whether a definition existed for that +# file. +# +sub DeleteDefinition #(ExtensionID extension, string itemString, FileName file) => bool + { + my ($self, $extension, $itemString, $file) = @_; + + my $item = $self->GetItem($extension, $itemString); + + if (!defined $item) + { return 0; }; + + my $result = $item->DeleteDefinition($file); + + if ($result) + { + $files{$file}->DeleteItem($extension, $itemString); + $extensions[$extension]->OnDeletedDefinition($itemString, $file, !$item->HasDefinitions()); + }; + + return $result; + }; + + +# +# Function: HasDefinitions +# +# Returns whether there are any definitions for this item. +# +sub HasDefinitions #(ExtensionID extension, string itemString) => bool + { + my ($self, $extension, $itemString) = @_; + + my $item = $self->GetItem($extension, $itemString); + + if (!defined $item) + { return 0; }; + + return $item->HasDefinitions(); + }; + + +# +# Function: HasDefinition +# +# Returns whether there is a definition for the passed <FileName>. +# +sub HasDefinition #(ExtensionID extension, string itemString, FileName file) => bool + { + my ($self, $extension, $itemString, $file) = @_; + + my $item = $self->GetItem($extension, $itemString); + + if (!defined $item) + { return 0; }; + + return $item->HasDefinition($file); + }; + + + +############################################################################### +# Group: Watched File Functions + + +# +# Function: WatchFileForChanges +# +# Begins watching a file for changes. Only one file at a time can be watched. +# +# This should be called before a file is parsed so the file info goes both into the main database and the watched file info. +# Afterwards you call <AnalyzeWatchedFileChanges()> so item deletions and definition changes can be detected. +# +# Parameters: +# +# filename - The <FileName> to watch. +# +sub WatchFileForChanges #(FileName filename) + { + my ($self, $filename) = @_; + + $watchedFileName = $filename; + $watchedFile = NaturalDocs::SourceDB::File->New(); + $watchedFileDefinitions = NaturalDocs::SourceDB::WatchedFileDefinitions->New(); + }; + + +# +# Function: WatchingFileForChanges +# +# Returns whether we're currently watching a file for changes or not. +# +sub WatchingFileForChanges # => bool + { + my $self = shift; + return defined $watchedFileName; + }; + + +# +# Function: AnalyzeWatchedFileChanges +# +# Analyzes the watched file for changes. Will delete and change definitions as necessary. +# +sub AnalyzeWatchedFileChanges + { + my $self = shift; + + if (!$self->WatchingFileForChanges()) + { die "Tried to analyze watched file for changes in SourceDB when no file was being watched."; }; + if (!$files{$watchedFileName}) + { return; }; + + + # Process extensions last registered to first. + + for (my $extension = scalar @extensions - 1; $extension >= 0; $extension--) + { + my @items = $files{$watchedFileName}->ListItems($extension); + + foreach my $item (@items) + { + if ($watchedFile->HasItem($extension, $item)) + { + if ($extensionUsesDefinitionObjects[$extension]) + { + my $originalDefinition = $items[$extension]->GetDefinition($watchedFileName); + my $watchedDefinition = $watchedFileDefinitions->GetDefinition($extension, $item); + + if (!$originalDefinition->Compare($watchedDefinition)) + { $self->ChangeDefinition($extension, $item, $watchedFileName, $watchedDefinition); }; + } + } + else # !$watchedFile->HasItem($item) + { + $self->DeleteDefinition($extension, $item, $watchedFileName); + }; + }; + }; + + + $watchedFile = undef; + $watchedFileName = undef; + $watchedFileDefinitions = undef; + }; + + +1; |