ddnet/docs/tool/Modules/NaturalDocs/ImageReferenceTable.pm
2008-08-02 08:21:29 +00:00

384 lines
12 KiB
Perl

###############################################################################
#
# Package: NaturalDocs::ImageReferenceTable
#
###############################################################################
#
# A <NaturalDocs::SourceDB>-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
#
# - <NaturalDocs::Project> and <NaturalDocs::SourceDB> must be initialized before this package can be used.
#
# - Call <Register()> before using.
#
#
# Topic: Programming Notes
#
# When working on this code, remember that there are three things it has to juggle.
#
# - The information in <NaturalDocs::SourceDB>.
# - Image file references in <NaturalDocs::Project>.
# - Source file rebuilding on changes.
#
# Managing the actual image files will be handled between <NaturalDocs::Project> and the <NaturalDocs::Builder>
# sub-packages.
#
#
# Topic: Implementation
#
# Managing image references is simpler than managing the references in <NaturalDocs::SymbolTable>. 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 <NaturalDocs::Project> 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 <NaturalDocs::Project>. However, it is not loaded and
# saved to disk by it. Rather, it is regenerated by this package when it loads <ImageReferenceTable.nd>.
# 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 <Load()> function, again since the list of targets (image files) is constant.
#
# The package is based on <NaturalDocs::SourceDB>, so read it's documentation for more information on how it works.
#
###############################################################################
# Group: Variables
#
# var: extensionID
# The <ExtensionID> granted by <NaturalDocs::SourceDB>.
#
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 <NaturalDocs::BinaryFile>.
#
# > [Image Reference String or undef]
# > [AString16: target file]
# > [UInt16: target width or 0]
# > [UInt16: target height or 0]
#
# For each <ImageReferenceString>, 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.
#
# <ImageReferenceStrings> are encoded by <NaturalDocs::ImageReferenceTable::String>.
#
# > [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 <ImageReferenceString> until it hits an undef.
#
# Revisions:
#
# 1.4:
#
# - The file was added to Natural Docs.
#
###############################################################################
# Group: Functions
#
# Function: Register
# Registers the package with <NaturalDocs::SourceDB>.
#
sub Register
{
my $self = shift;
$extensionID = NaturalDocs::SourceDB->RegisterExtension($self, 0);
};
#
# Function: Load
#
# Loads the data from <ImageReferenceTable.nd>. 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 <ImageReferenceTable.nd>.
#
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 <NaturalDocs::SourceDB>. 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 <FileName> 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 <ImageReferenceString> and sets it on the
# <NaturalDocs::ImageReferenceTable::Reference> object. Returns the new target <FileName>. 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;