<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">###############################################################################
#
#   Package: NaturalDocs::ImageReferenceTable
#
###############################################################################
#
#   A &lt;NaturalDocs::SourceDB&gt;-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
#
#       - &lt;NaturalDocs::Project&gt; and &lt;NaturalDocs::SourceDB&gt; must be initialized before this package can be used.
#
#       - Call &lt;Register()&gt; before using.
#
#
#   Topic: Programming Notes
#
#       When working on this code, remember that there are three things it has to juggle.
#
#       - The information in &lt;NaturalDocs::SourceDB&gt;.
#       - Image file references in &lt;NaturalDocs::Project&gt;.
#       - Source file rebuilding on changes.
#
#       Managing the actual image files will be handled between &lt;NaturalDocs::Project&gt; and the &lt;NaturalDocs::Builder&gt;
#       sub-packages.
#
#
#   Topic: Implementation
#
#       Managing image references is simpler than managing the references in &lt;NaturalDocs::SymbolTable&gt;.  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 &lt;NaturalDocs::Project&gt; 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 &lt;NaturalDocs::Project&gt;.  However, it is not loaded and
#       saved to disk by it.  Rather, it is regenerated by this package when it loads &lt;ImageReferenceTable.nd&gt;.
#       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 &lt;Load()&gt; function, again since the list of targets (image files) is constant.
#
#       The package is based on &lt;NaturalDocs::SourceDB&gt;, so read it's documentation for more information on how it works.
#


###############################################################################
# Group: Variables


#
#   var: extensionID
#   The &lt;ExtensionID&gt; granted by &lt;NaturalDocs::SourceDB&gt;.
#
my $extensionID;



###############################################################################
# Group: Files


#
#   File: ImageReferenceTable.nd
#
#   The data file which stores all the image references from the last run of Natural Docs.
#
#   Format:
#
#       &gt; [Standard Binary Header]
#
#       It starts with the standard binary header from &lt;NaturalDocs::BinaryFile&gt;.
#
#       &gt; [Image Reference String or undef]
#       &gt; [AString16: target file]
#       &gt; [UInt16: target width or 0]
#       &gt; [UInt16: target height or 0]
#
#       For each &lt;ImageReferenceString&gt;, 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.
#
#       &lt;ImageReferenceStrings&gt; are encoded by &lt;NaturalDocs::ImageReferenceTable::String&gt;.
#
#       &gt; [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 &lt;ImageReferenceString&gt; until it hits an undef.
#
#	Revisions:
#
#		1.4:
#
#			- The file was added to Natural Docs.
#



###############################################################################
# Group: Functions


#
#   Function: Register
#   Registers the package with &lt;NaturalDocs::SourceDB&gt;.
#
sub Register
    {
    my $self = shift;
    $extensionID = NaturalDocs::SourceDB-&gt;RegisterExtension($self, 0);
    };


#
#   Function: Load
#
#   Loads the data from &lt;ImageReferenceTable.nd&gt;.  Returns whether it was successful.
#
sub Load # =&gt; bool
    {
    my $self = shift;

    if (NaturalDocs::Settings-&gt;RebuildData())
        {  return 0;  };

    # The file format hasn't changed since it was introduced.
    if (!NaturalDocs::BinaryFile-&gt;OpenForReading( NaturalDocs::Project-&gt;DataFile('ImageReferenceTable.nd') ))
        {  return 0;  };


    # [Image Reference String or undef]
    while (my $referenceString = NaturalDocs::ImageReferenceTable::String-&gt;FromBinaryFile())
        {
        NaturalDocs::SourceDB-&gt;AddItem($extensionID, $referenceString,
                                                           NaturalDocs::ImageReferenceTable::Reference-&gt;New());

        # [AString16: target file]
        # [UInt16: target width or 0]
        # [UInt16: target height or 0]

        my $targetFile = NaturalDocs::BinaryFile-&gt;GetAString16();
        my $width = NaturalDocs::BinaryFile-&gt;GetUInt16();
        my $height = NaturalDocs::BinaryFile-&gt;GetUInt16();

        my $newTargetFile = $self-&gt;SetReferenceTarget($referenceString);
        my $newWidth;
        my $newHeight;

        if ($newTargetFile)
            {
            NaturalDocs::Project-&gt;AddImageFileReference($newTargetFile);
            ($newWidth, $newHeight) = NaturalDocs::Project-&gt;ImageFileDimensions($newTargetFile);
            };

        my $rebuildDefinitions = ($newTargetFile ne $targetFile || $newWidth != $width || $newHeight != $height);


        # [AString16: definition file or undef] ...
        while (my $definitionFile = NaturalDocs::BinaryFile-&gt;GetAString16())
            {
            NaturalDocs::SourceDB-&gt;AddDefinition($extensionID, $referenceString, $definitionFile);

            if ($rebuildDefinitions)
                {  NaturalDocs::Project-&gt;RebuildFile($definitionFile);  };
            };
        };


    NaturalDocs::BinaryFile-&gt;Close();
    return 1;
    };


#
#   Function: Save
#
#   Saves the data to &lt;ImageReferenceTable.nd&gt;.
#
sub Save
    {
    my $self = shift;

    my $references = NaturalDocs::SourceDB-&gt;GetAllItemsHashRef($extensionID);

    NaturalDocs::BinaryFile-&gt;OpenForWriting( NaturalDocs::Project-&gt;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-&gt;ToBinaryFile($referenceString);

        my $target = $referenceObject-&gt;Target();
        my ($width, $height);

        if ($target)
            {  ($width, $height) = NaturalDocs::Project-&gt;ImageFileDimensions($target);  };

        NaturalDocs::BinaryFile-&gt;WriteAString16( $referenceObject-&gt;Target() );
        NaturalDocs::BinaryFile-&gt;WriteUInt16( ($width || 0) );
        NaturalDocs::BinaryFile-&gt;WriteUInt16( ($height || 0) );

        # [AString16: definition file or undef] ...

        my $definitions = $referenceObject-&gt;GetAllDefinitionsHashRef();

        foreach my $definition (keys %$definitions)
            {  NaturalDocs::BinaryFile-&gt;WriteAString16($definition);  };

        NaturalDocs::BinaryFile-&gt;WriteAString16(undef);
        };

    NaturalDocs::ImageReferenceTable::String-&gt;ToBinaryFile(undef);

    NaturalDocs::BinaryFile-&gt;Close();
    };


#
#   Function: AddReference
#
#   Adds a new image reference.
#
sub AddReference #(FileName file, string referenceText)
    {
    my ($self, $file, $referenceText) = @_;

    my $referenceString = NaturalDocs::ImageReferenceTable::String-&gt;Make($file, $referenceText);

    if (!NaturalDocs::SourceDB-&gt;HasItem($extensionID, $referenceString))
        {
        my $referenceObject = NaturalDocs::ImageReferenceTable::Reference-&gt;New();
        NaturalDocs::SourceDB-&gt;AddItem($extensionID, $referenceString, $referenceObject);

        my $target = $self-&gt;SetReferenceTarget($referenceString);
        if ($target)
            {  NaturalDocs::Project-&gt;AddImageFileReference($target);  };
        };

    NaturalDocs::SourceDB-&gt;AddDefinition($extensionID, $referenceString, $file);
    };


#
#   Function: OnDeletedDefinition
#
#   Called for each definition deleted by &lt;NaturalDocs::SourceDB&gt;.  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-&gt;GetItem($extensionID, $referenceString);
        my $target = $referenceObject-&gt;Target();

        if ($target)
            {  NaturalDocs::Project-&gt;DeleteImageFileReference($target);  };

        NaturalDocs::SourceDB-&gt;DeleteItem($extensionID, $referenceString);
        };
    };


#
#   Function: GetReferenceTarget
#
#   Returns the image file the reference resolves to, or undef if none.
#
#   Parameters:
#
#       sourceFile - The source &lt;FileName&gt; the reference appears in.
#       text - The reference text.
#
sub GetReferenceTarget #(FileName sourceFile, string text) =&gt; FileName
    {
    my ($self, $sourceFile, $text) = @_;

    my $referenceString = NaturalDocs::ImageReferenceTable::String-&gt;Make($sourceFile, $text);
    my $reference = NaturalDocs::SourceDB-&gt;GetItem($extensionID, $referenceString);

    if (!defined $reference)
        {  return undef;  }
    else
        {  return $reference-&gt;Target();  };
    };


#
#   Function: SetReferenceTarget
#
#   Determines the best target for the passed &lt;ImageReferenceString&gt; and sets it on the
#   &lt;NaturalDocs::ImageReferenceTable::Reference&gt; object.  Returns the new target &lt;FileName&gt;.  Does *not* add any source
#   files to the bulid list.
#
sub SetReferenceTarget #(ImageReferenceString referenceString) =&gt; FileName
    {
    my ($self, $referenceString) = @_;

    my $referenceObject = NaturalDocs::SourceDB-&gt;GetItem($extensionID, $referenceString);
    my ($sourcePath, $text) = NaturalDocs::ImageReferenceTable::String-&gt;InformationOf($referenceString);


    # Try the path relative to the source file first.

    my $target;

    my $imageFile = NaturalDocs::File-&gt;JoinPaths($sourcePath, $text);
    my $exists = NaturalDocs::Project-&gt;ImageFileExists($imageFile);


    # Then try relative image directories.

    if (!$exists)
        {
        my $relativeImageDirectories = NaturalDocs::Settings-&gt;RelativeImageDirectories();

        for (my $i = 0; $i &lt; scalar @$relativeImageDirectories &amp;&amp; !$exists; $i++)
            {
            $imageFile = NaturalDocs::File-&gt;JoinPaths($sourcePath, $relativeImageDirectories-&gt;[$i], 1);
            $imageFile = NaturalDocs::File-&gt;JoinPaths($imageFile, $text);

            $exists = NaturalDocs::Project-&gt;ImageFileExists($imageFile);
            };
        };


    # Then try absolute image directories.

    if (!$exists)
        {
        my $imageDirectories = NaturalDocs::Settings-&gt;ImageDirectories();

        for (my $i = 0; $i &lt; scalar @$imageDirectories &amp;&amp; !$exists; $i++)
            {
            $imageFile = NaturalDocs::File-&gt;JoinPaths($imageDirectories-&gt;[$i], $text);
            $exists = NaturalDocs::Project-&gt;ImageFileExists($imageFile);
            };
        };


    if ($exists)
        {  $target = NaturalDocs::Project-&gt;ImageFileCapitalization($imageFile);  };
    #else leave it as undef.

    $referenceObject-&gt;SetTarget($target);
    return $target;
    };


1;
</pre></body></html>