############################################################################### # # Package: NaturalDocs::ImageReferenceTable # ############################################################################### # # A -based package that manages all the image references appearing in source files. # ############################################################################### # 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::ImageReferenceTable::String; use NaturalDocs::ImageReferenceTable::Reference; package NaturalDocs::ImageReferenceTable; use base 'NaturalDocs::SourceDB::Extension'; ############################################################################### # Group: Information # # Topic: Usage # # - and must be initialized before this package can be used. # # - Call before using. # # # Topic: Programming Notes # # When working on this code, remember that there are three things it has to juggle. # # - The information in . # - Image file references in . # - Source file rebuilding on changes. # # Managing the actual image files will be handled between and the # sub-packages. # # # Topic: Implementation # # Managing image references is simpler than managing the references in . In SymbolTable, # you have to worry about reference targets popping into and out of existence. A link may go to a file that hasn't been # reparsed yet and the target may no longer exist. We have to deal with that when we know it, which may be after the # reference's file was parsed. Also, a new definition may appear that serves as a better interpretation of a link than its # current target, and again we may only know that after the reference's file has been parsed already. So we have to deal # with scores and potential symbols and each symbol knowing exactly what links to it and so forth. # # Not so with image references. All possible targets (all possible image files) are known by early # on and will remain consistent throughout execution. So because of that, we can get away with only storing reference # counts with each image and determining exactly where a reference points to as we find them. # # Reference counts are stored with the image file information in . However, it is not loaded and # saved to disk by it. Rather, it is regenerated by this package when it loads . # NaturalDocs::Project only stores the last modification time (so it can add files to the build list if they've changed) and # whether it had any references at all on the last run (so it knows whether it should care if they've changed.) # ImageReferenceTable.nd stores each reference's target, width, and height. Whether their interpretations have changed is # dealt with in the function, again since the list of targets (image files) is constant. # # The package is based on , so read it's documentation for more information on how it works. # ############################################################################### # Group: Variables # # var: extensionID # The granted by . # my $extensionID; ############################################################################### # Group: Files # # File: ImageReferenceTable.nd # # The data file which stores all the image references from the last run of Natural Docs. # # Format: # # > [Standard Binary Header] # # It starts with the standard binary header from . # # > [Image Reference String or undef] # > [AString16: target file] # > [UInt16: target width or 0] # > [UInt16: target height or 0] # # For each , it's target, width, and height are stored. The target is needed so we can tell if it # changed from the last run, and the dimensions are needed because if the target hasn't changed but the file's dimensions # have, the source files need to be rebuilt. # # are encoded by . # # > [AString16: definition file or undef] ... # # Then comes a series of AString16s for all the files that define the reference until it hits an undef. # # This whole series is repeated for each until it hits an undef. # # Revisions: # # 1.4: # # - The file was added to Natural Docs. # ############################################################################### # Group: Functions # # Function: Register # Registers the package with . # sub Register { my $self = shift; $extensionID = NaturalDocs::SourceDB->RegisterExtension($self, 0); }; # # Function: Load # # Loads the data from . Returns whether it was successful. # sub Load # => bool { my $self = shift; if (NaturalDocs::Settings->RebuildData()) { return 0; }; # The file format hasn't changed since it was introduced. if (!NaturalDocs::BinaryFile->OpenForReading( NaturalDocs::Project->DataFile('ImageReferenceTable.nd') )) { return 0; }; # [Image Reference String or undef] while (my $referenceString = NaturalDocs::ImageReferenceTable::String->FromBinaryFile()) { NaturalDocs::SourceDB->AddItem($extensionID, $referenceString, NaturalDocs::ImageReferenceTable::Reference->New()); # [AString16: target file] # [UInt16: target width or 0] # [UInt16: target height or 0] my $targetFile = NaturalDocs::BinaryFile->GetAString16(); my $width = NaturalDocs::BinaryFile->GetUInt16(); my $height = NaturalDocs::BinaryFile->GetUInt16(); my $newTargetFile = $self->SetReferenceTarget($referenceString); my $newWidth; my $newHeight; if ($newTargetFile) { NaturalDocs::Project->AddImageFileReference($newTargetFile); ($newWidth, $newHeight) = NaturalDocs::Project->ImageFileDimensions($newTargetFile); }; my $rebuildDefinitions = ($newTargetFile ne $targetFile || $newWidth != $width || $newHeight != $height); # [AString16: definition file or undef] ... while (my $definitionFile = NaturalDocs::BinaryFile->GetAString16()) { NaturalDocs::SourceDB->AddDefinition($extensionID, $referenceString, $definitionFile); if ($rebuildDefinitions) { NaturalDocs::Project->RebuildFile($definitionFile); }; }; }; NaturalDocs::BinaryFile->Close(); return 1; }; # # Function: Save # # Saves the data to . # sub Save { my $self = shift; my $references = NaturalDocs::SourceDB->GetAllItemsHashRef($extensionID); NaturalDocs::BinaryFile->OpenForWriting( NaturalDocs::Project->DataFile('ImageReferenceTable.nd') ); while (my ($referenceString, $referenceObject) = each %$references) { # [Image Reference String or undef] # [AString16: target file] # [UInt16: target width or 0] # [UInt16: target height or 0] NaturalDocs::ImageReferenceTable::String->ToBinaryFile($referenceString); my $target = $referenceObject->Target(); my ($width, $height); if ($target) { ($width, $height) = NaturalDocs::Project->ImageFileDimensions($target); }; NaturalDocs::BinaryFile->WriteAString16( $referenceObject->Target() ); NaturalDocs::BinaryFile->WriteUInt16( ($width || 0) ); NaturalDocs::BinaryFile->WriteUInt16( ($height || 0) ); # [AString16: definition file or undef] ... my $definitions = $referenceObject->GetAllDefinitionsHashRef(); foreach my $definition (keys %$definitions) { NaturalDocs::BinaryFile->WriteAString16($definition); }; NaturalDocs::BinaryFile->WriteAString16(undef); }; NaturalDocs::ImageReferenceTable::String->ToBinaryFile(undef); NaturalDocs::BinaryFile->Close(); }; # # Function: AddReference # # Adds a new image reference. # sub AddReference #(FileName file, string referenceText) { my ($self, $file, $referenceText) = @_; my $referenceString = NaturalDocs::ImageReferenceTable::String->Make($file, $referenceText); if (!NaturalDocs::SourceDB->HasItem($extensionID, $referenceString)) { my $referenceObject = NaturalDocs::ImageReferenceTable::Reference->New(); NaturalDocs::SourceDB->AddItem($extensionID, $referenceString, $referenceObject); my $target = $self->SetReferenceTarget($referenceString); if ($target) { NaturalDocs::Project->AddImageFileReference($target); }; }; NaturalDocs::SourceDB->AddDefinition($extensionID, $referenceString, $file); }; # # Function: OnDeletedDefinition # # Called for each definition deleted by . This is called *after* the definition has been deleted from # the database, so don't expect to be able to read it. # sub OnDeletedDefinition #(ImageReferenceString referenceString, FileName file, bool wasLastDefinition) { my ($self, $referenceString, $file, $wasLastDefinition) = @_; if ($wasLastDefinition) { my $referenceObject = NaturalDocs::SourceDB->GetItem($extensionID, $referenceString); my $target = $referenceObject->Target(); if ($target) { NaturalDocs::Project->DeleteImageFileReference($target); }; NaturalDocs::SourceDB->DeleteItem($extensionID, $referenceString); }; }; # # Function: GetReferenceTarget # # Returns the image file the reference resolves to, or undef if none. # # Parameters: # # sourceFile - The source the reference appears in. # text - The reference text. # sub GetReferenceTarget #(FileName sourceFile, string text) => FileName { my ($self, $sourceFile, $text) = @_; my $referenceString = NaturalDocs::ImageReferenceTable::String->Make($sourceFile, $text); my $reference = NaturalDocs::SourceDB->GetItem($extensionID, $referenceString); if (!defined $reference) { return undef; } else { return $reference->Target(); }; }; # # Function: SetReferenceTarget # # Determines the best target for the passed and sets it on the # object. Returns the new target . Does *not* add any source # files to the bulid list. # sub SetReferenceTarget #(ImageReferenceString referenceString) => FileName { my ($self, $referenceString) = @_; my $referenceObject = NaturalDocs::SourceDB->GetItem($extensionID, $referenceString); my ($sourcePath, $text) = NaturalDocs::ImageReferenceTable::String->InformationOf($referenceString); # Try the path relative to the source file first. my $target; my $imageFile = NaturalDocs::File->JoinPaths($sourcePath, $text); my $exists = NaturalDocs::Project->ImageFileExists($imageFile); # Then try relative image directories. if (!$exists) { my $relativeImageDirectories = NaturalDocs::Settings->RelativeImageDirectories(); for (my $i = 0; $i < scalar @$relativeImageDirectories && !$exists; $i++) { $imageFile = NaturalDocs::File->JoinPaths($sourcePath, $relativeImageDirectories->[$i], 1); $imageFile = NaturalDocs::File->JoinPaths($imageFile, $text); $exists = NaturalDocs::Project->ImageFileExists($imageFile); }; }; # Then try absolute image directories. if (!$exists) { my $imageDirectories = NaturalDocs::Settings->ImageDirectories(); for (my $i = 0; $i < scalar @$imageDirectories && !$exists; $i++) { $imageFile = NaturalDocs::File->JoinPaths($imageDirectories->[$i], $text); $exists = NaturalDocs::Project->ImageFileExists($imageFile); }; }; if ($exists) { $target = NaturalDocs::Project->ImageFileCapitalization($imageFile); }; #else leave it as undef. $referenceObject->SetTarget($target); return $target; }; 1;