#! /usr/bin/perl -w # # Class name: HeaderElement # Synopsis: Root class for Function, Typedef, Constant, etc. -- used by HeaderDoc. # # Last Updated: $Date: 2014/03/06 11:05:55 $ # # Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. # # The Original Code and all software distributed under the License are # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, # INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. # # @APPLE_LICENSE_HEADER_END@ # ###################################################################### # /*! @header # @abstract # HeaderElement class package file. # @discussion # This file contains the HeaderElement class, the base class # for all API elements. # # For details, see the class documentation below. # @indexgroup HeaderDoc API Objects # */ # /*! # @abstract # Base class for all API objects. # @discussion # The HeaderElement class is the base class for all objects # representing API elements, including headers, structs, # functions, classes, etc. The majority of HeaderDoc # classes are subclasses of this class. # # This class provides services common to all API elements # (or services common to several elements). The most # important functions in this class are: # #
#
{@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/processComment//() processComment}
#
Parses a HeaderDoc comment block.
#
{@link declarationInHTML}
#
Returns the declaration in HTML/XML by calling into the # {@link //apple_ref/perl/cl/HeaderDoc::ParseTree ParseTree} # class.
#
{@link documentationBlock} and {@link XMLdocumentationBlock}
#
Return the entire documentation output for this # object (class, function, enumeration, data structure, and so on) and any # descendants enclosed within it.
#
{@link keywords}
#
Returns a set of keywords for parsing declarations in # the current programming language and returns whether # those keywords should be interpreted in a case-sensitive # or case-insensitive fashion.
#
{@link apirefSetup}
#
Does a lot of the work setting up the API reference for # this object and its subclasses.
#
{@link apiref} and {@link apiuid}
#
Get and set the API reference, respectively.
#
# # This API object type should never actually be emitted as output; only # its subclasses are relevant. # # @var ABSTRACT # The contents of the associated \@abstract tag. # @var ABSTRACTLOCKED # Temporary storage for the ABSTRACT field, used when fields are locked # during define block parsing. For more information, see the # documentation for the {@link discussionLocked} and # {@link unlockDiscussion} functions. # @var ACCESSCONTROL # The access control state for this API element (e.g. public, private...) # @var APIOWNER # The {@link //apple_ref/perl/cl/HeaderDoc::APIOwner APIOwner} object # whose declaration includes this one. This owning object may be a # class, header, protocol, category, etc. # @var APIREFSETUPDONE # Set to 1 when the {@link apirefSetup} call finishes. This avoids # doing unnecessary work (and makes the UID collision protection easier). # @var APIUID # A cache of the raw UID for this object. Set by {@link apiuid}. This # is not actually used because it is often wrong. # @var APPLEREFISDOC # Holds 1 if this object should emit a doc-style API reference, # else 0. # @var ATTRIBUTELISTS # The list-style attributes associated with this API element. # See {@link attributelist}, {@link getAttributeLists}, and # {@link checkAttributeLists}. # @var AUTORELATE # Used to store a list of automatically-generated cross-references # for blocks containing mixed types. For more information, see the # documentation for the {@link autoRelate} function. # @var AVAILABILITY # The availability information for this element (from \@availability # or from availability macros. # @var BLOCKOFFSET # The offset of the start of the block of lines in which this declaration # appears. (Added to RAWLINENUM to get the actual line # number.) # @var CASESENSITIVE # A cache of the value for case sensitivity from the last time the # {@link keywords} method was called. # @var CLASS # The HeaderDoc object class for this object (e.g. HeaderDoc::Method). # Used to bless the object properly from a hash reference. # @var CONSTANTS # An array of constants in this enumeration or API owner object. Each # item in this array is a # {@link //apple_ref/perl/cl/HeaderDoc::MinorAPIElement MinorAPIElement} # object. # @var DECLARATION # The (vestigial) text declaration for this element. # @var DECLARATIONINHTML # A cache of the HTML declaration for this element. # @var DISCUSSION # The contents of the \@discussion tag (or untagged content elsewhere # in the comment). # @var DISCUSSION_SET # Set to 1 when an actual discussion has been seen. This is used # to determine whether additional words in the name line should be # treated as discussion or part of the name. See {@link nameline_discussion} # for more info. # @var DISCUSSIONLOCKED # Temporary storage for the DISCUSSION field, used when fields are locked # during define block parsing. For more information, see the # documentation for the {@link discussionLocked} and # {@link unlockDiscussion} functions. # @var DISCUSSION_SETLOCKED # Temporary storage for the DISCUSSION_SET field, used when fields are locked # during define block parsing. For more information, see the # documentation for the {@link discussionLocked} and # {@link unlockDiscussion} functions. # @var FIELDHEADING # A cache of the last field heading displayed. Unused. # @var FIELDS # An array of fields in this struct, union, or typedef. # @var FILENAME # The filename containing this declaration (with leading path parts removed). # @var FIRSTCONSTNAME # The first constant name within an enumeration. Used as the name of # an anonymous enumeration if no name is specified in the comment. # @var FORCENAME # The contents of an \@name tag, which overrides any name obtained in any other way. # @var FULLPATH # The filename containing this declaration (with leading parts left intact). # @var FUNCORMETHOD # A cache of the last func_or_method value returned by # {@link apirefSetup}. Unused. # @var FUNCTIONCONTENTS # The contents within the outer braces of a function. # @var GROUP # The name of the (documentation) group that this object is in. # @var HASPROCESSEDCOMMENT # Set to 1 after the # {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/processComment//() processComment} # function has executed. This prevents doing that twice (which would cause # a number of problems). # @var HIDECONTENTS # Set to 1 if the \@hidecontents tag is found in a macro's HeaderDoc comment. # See {@link hideContents}. # @var HIDEDOC # Set by the block parser to indicate that this object should not emit any # documentation. This is set on #define objects within a # #define block if the HIDESINGLETONS flag is set # on the enclosing block. # @var HIDESINGLETONS # This flag is set (to 1) if the \@hidesingletons tag is found inside an # \@defineblock (or \@definedblock) comment. # @var INCLUDED_DEFINES # An array of #define objects nested in an \@defineblock # (or \@definedblock). # @var INDEFINEBLOCK # Set on individual #define objects within an \@defineblock # (or \@definedblock). # @var INDEXGROUP # The group in which this object and its descendants should appear inside # the master TOC as generated by # {@link //apple_ref/doc/header/gatherHeaderDoc.pl gatherHeaderDoc.pl}. # @var INHERITDOC # The discussion for the superclass. Inserted if requested. See # {@link fixup_inheritDoc}. # @var INSERTED # Set to 42 once this object has been added to its enclosing API owner. # @var ISBLOCK # Set to 1 for #define blocks, 2 for #ifdef blocks around # multiple variants of a function. # @var ISCALLBACK # Set to 1 for a typedef of a callback. # @var ISDEFINE # Set for #define members in a non-#define block to prevent # lovely problems. # @var ISFUNCPTR # Set to 1 for a variable or type definition if the \@result tag is included. # @var ISINTERNAL # Set to 1 for internal variables/functions that should only be # documented if a special flag is set. # @var ISTEMPLATE # Set for functions that have template parameters. # @var KEEPCONSTANTS # A cached array of constants generated by {@link apirefSetup}. # @var KEEPFIELDS # A cached array of fields generated by {@link apirefSetup}. # @var KEEPPARAMS # A cached array of parameters generated by {@link apirefSetup}. # @var KEEPVARIABLES # A cached array of variables generated by {@link apirefSetup}. # @var KEYWORDHASH # A cache of the keyword hash from the last time the # {@link keywords} method was called. # @var LANG # The programming language for this declaration. # @var LINENUM # The line number in which this declaration begins relative to the start # of the header. (This is the sum ofBLOCKOFFSET and # RAWLINENUM.) # @var LINKAGESTATE # The linkage state. Unused. # @var LINKUID # A cache of the "link UID". See {@link generateLinkUID}. # @var LONGATTRIBUTES # An array of "long" attributes (those containing multiple paragraphs # as opposed to just a line or so). # @var MAINOBJECT # The main object (block object) in a block declaration. # @var MASTERENUM # The main enum object that owns the enclosing constants. # Originally used for apple_ref emission purposes. Unused. # @var NAME # The name of the API symbol that this object represents. # Usually accessed with # {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/name//() name}. # @var NAMELINE_DISCUSSION # The portion of the discussion that appears on the same line as # the name in an old-style HeaderDoc comment. See {@link nameline_discussion} # for more information. # @var NAMEREFS # An array of names for this object that appear within the # {@link HeaderDoc::namerefs} array. Used when # destroying this object to allow those references to be destroyed. # @var NAMESPACE # Contains a text string representing the namespace for this class. # See {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/namespace//() namespace}. # @var NOREGISTERUID # Set to 1 when an object's UID has been unregistered to prevent it from being # registered again. See {@link noRegisterUID}. # @var ORIGCLASS # The name of the class that a method, variable, etc. was inherited from. # See {@link origClass}. # @var ORIGTYPE # The type of the original object (generated based on the HeaderDoc comment) # from which this object (of a different type) was later cloned. For more # information, see the documentation for the {@link origType} function. # @var OUTPUTFORMAT # The current output format ("html" or "hdxml"). # @var PARSEDPARAMETERS # For methods, an array of parsed parameters for a method. For # a structure, union, or type definition, an array of parsed # fields. For an enumaration, an array of parsed constants. # @var PARSERSTATE # The parser state object associated with this API object. # @var PARSETREE # The parse tree object containing the declaration for this API object. # @var PARSETREECLEANUP # An array of parse tree objects that reference this API object. Used when # destroying this object to allow those references to be destroyed. # @var PARSETREELIST # An array of parse trees containing the declarations for # API objects within this block-style API object. Only # relevant if {@link isBlock} returns 1. # @var PPFIXUPDONE # Set to 1 to indicate that {@link fixupParsedParameters} has # already been called on this object. # @var PRESERVESPACES # Set to 1 to indicate that the user wants to preserve # whitespace within this declaration. See {@link preserve_spaces}. # @var PRIVATEDECLARATION # The private declaration portion of a C++ method (after the colon). # @var RAWLINENUM # The line number in which this declaration begins relative to the start # of the block of lines. (Added to BLOCKOFFSET to get the actual # line number.) # @var RAWNAME # See {@link rawname}. # @var REQUESTEDUID # A UID explicitly provided with the \@apiuid tag. See {@link requestedUID}. # @var RETURNTYPE # The return type for a function (parsed from the code). # @var SEEALSODUPCHECK # Used to prevent duplicates in auto-generated cross-linking. # See {@link seeDupCheck} for details. # @var SINGLEATTRIBUTES # An array of "short" attributes (those containing just a word or a line # as opposed to multiple paragraphs). # @var SUBLANG # The programming language variant for this object (e.g. cpp for C++). # @var SUPPRESSCHILDREN # Indicates that UIDs for child elements (e.g. fields) should be suppressed # in the HTML output. See {@link suppressChildren} for more information. # @var TAGGEDPARAMETERS # An array of explicitly tagged (\@param) parameters in a function/method. # @var THROWS # The content of the \@throws tag in the comment (with HTML formatting added). # See also XMLTHROWS below. # @var TPCDONE # Set to 1 after {@link taggedParsedCompare} is called so that this # isn't done repeatedly for the same function, union, struct, or typedef. # @var TYPEDEFCONTENTS # The contents of a typedef declaration. # @var UPDATED # The last updated date. See # {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/updated//() updated}. # @var USESTDOUT # Set to 1 if the -P (pipe) flag is passed to HeaderDoc, else 0. # @var VALUE # The value of a constant/variable. # See {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/value//() value}. # @var VARIABLES # An array of variables enclosed in a normal (usually function) object. These are # {@link //apple_ref/perl/cl/HeaderDoc::MinorAPIElement MinorAPIElement} objects. # @var VARS # An array of variables enclosed in an API owner object. These are # {@link //apple_ref/perl/cl/HeaderDoc::Var Var} objects. # @var XMLTHROWS # A copy of the value in THROWS with XML formatting. # @var AS_CLASS_SELF # A CPPClass object cloned from the current function object that holds # any scripts that are nested within the function. See # {@link cloneAppleScriptFunctionContents} and {@link processAppleScriptFunctionContents} # for more information. # @var AS_FUNC_SELF # The Function object that the current class object was cloned from. See # {@link cloneAppleScriptFunctionContents} and {@link processAppleScriptFunctionContents} # for more information. # @var ASCONTENTSPROCESSED # Set to 1 after the {@link processAppleScriptFunctionContents} method runs. # for more information. # @var PARSEDPSEUDOCLASSNAME # The name of the directory where the contents from any classes within the {@link AS_CLASS_SELF} # container are written. # */ package HeaderDoc::HeaderElement; use HeaderDoc::Utilities qw(findRelativePath safeName getAPINameAndDisc printArray printHash unregisterUID registerUID html2xhtml sanitize parseTokens unregister_force_uid_clear dereferenceUIDObject filterHeaderDocTagContents validTag printFields splitOnPara getDefaultEncoding html_fixup_links xml_fixup_links); use File::Basename; use strict; use vars qw($VERSION @ISA); use POSIX qw(strftime mktime localtime); use Carp qw(cluck); use URI::Escape; use locale; use Encode qw(encode decode); use Devel::Peek; my $isMacOS; my $pathSeparator; if ($^O =~ /MacOS/io) { $pathSeparator = ":"; $isMacOS = 1; } else { $pathSeparator = "/"; $isMacOS = 0; } # /*! # @abstract # The revision control revision number for this module. # @discussion # In the git repository, contains the number of seconds since # January 1, 1970. # */ $HeaderDoc::HeaderElement::VERSION = '$Revision: 1394132755 $'; # /*! # @abstract # Creates a new HeaderElement object. # @param param # A reference to the relevant package object (e.g. # HeaderDoc::HeaderElement->new() to allocate # a new instance of this class). # */ sub new { my($param) = shift; my($class) = ref($param) || $param; my $self = {}; # cluck("Created header element\n"); # @@@ bless($self, $class); $self->_initialize(); # Now grab any key => value pairs passed in my (%attributeHash) = @_; foreach my $key (keys(%attributeHash)) { $self->{$key} = $attributeHash{$key}; } # if ((!$self->{LANG}) || (!$self->{SUBLANG})) { # cluck("Allocation with no language or sublanguage.\n"); # } # if ($self->{LANG} ne $HeaderDoc::lang) { # cluck("Allocation with non-matching language. ".$self->{LANG}." != ".$HeaderDoc::lang."\n"); # } # if ($self->{SUBLANG} ne $HeaderDoc::sublang) { # cluck("Allocation with non-matching sublanguage. ".$self->{SUBLANG}." != ".$HeaderDoc::sublang."\n"); # } return ($self); } # /*! # @abstract # Initializes an instance of a HeaderElement object. # @param self # The object to initialize. # */ sub _initialize { my($self) = shift; # $self->{ABSTRACT} = undef; # $self->{DISCUSSION} = undef; # $self->{DECLARATION} = undef; # $self->{DECLARATIONINHTML} = undef; # $self->{PRIVATEDECLARATION} = undef; # $self->{OUTPUTFORMAT} = undef; # $self->{FILENAME} = undef; # $self->{NAME} = undef; # $self->{RAWNAME} = undef; $self->{GROUP} = $HeaderDoc::globalGroup; $self->{INDEXGROUP} = ""; # $self->{THROWS} = undef; # $self->{XMLTHROWS} = undef; # $self->{UPDATED} = undef; # $self->{LINKAGESTATE} = undef; # $self->{ACCESSCONTROL} = undef; $self->{AVAILABILITY} = ""; # These must now be passed in via the new() parameters. # $self->{LANG} = $HeaderDoc::lang; # $self->{SUBLANG} = $HeaderDoc::sublang; $self->{SINGLEATTRIBUTES} = (); $self->{LONGATTRIBUTES} = (); # $self->{ATTRIBUTELISTS} = undef; $self->{APIOWNER} = $HeaderDoc::currentClass; # $self->{APIUID} = undef; # $self->{LINKUID} = undef; $self->{ORIGCLASS} = ""; # $self->{ISTEMPLATE} = 0; $self->{VALUE} = "UNKNOWN"; $self->{RETURNTYPE} = ""; $self->{TAGGEDPARAMETERS} = (); $self->{PARSEDPARAMETERS} = (); $self->{CONSTANTS} = (); $self->{VARIABLES} = (); # print STDERR "Initted VARIABLES in $self\n"; # $self->{LINENUM} = 0; $self->{CLASS} = "HeaderDoc::HeaderElement"; # $self->{CASESENSITIVE} = undef; # $self->{KEYWORDHASH} = undef; # $self->{MASTERENUM} = 0; # $self->{APIREFSETUPDONE} = 0; # $self->{TPCDONE} = 0; # $self->{NOREGISTERUID} = 0; # $self->{SUPPRESSCHILDREN} = 0; # $self->{NAMELINE_DISCUSSION} = undef; } my %CSS_STYLES = (); # /*! # @abstract # Duplicates this HeaderElement object into another one. # @param self # The object to clone. # @param clone # The victim object. # */ sub clone { my $self = shift; my $clone = undef; if (@_) { $clone = shift; } else { $clone = $self->new("lang" => $self->{LANG}, "sublang" => $self->{SUBLANG}); $clone->{CLASS} = $self->{CLASS}; } # $self->SUPER::clone($clone); # now clone stuff specific to header element $clone->{ABSTRACT} = $self->{ABSTRACT}; $clone->{DISCUSSION} = $self->{DISCUSSION}; $clone->{DECLARATION} = $self->{DECLARATION}; $clone->{DECLARATIONINHTML} = $self->{DECLARATIONINHTML}; $clone->{PRIVATEDECLARATION} = $self->{PRIVATEDECLARATION}; $clone->{OUTPUTFORMAT} = $self->{OUTPUTFORMAT}; $clone->{FILENAME} = $self->{FILENAME}; $clone->{FULLPATH} = $self->{FULLPATH}; $clone->{NAME} = $self->{NAME}; $clone->{RAWNAME} = $self->{RAWNAME}; $clone->{GROUP} = $self->{GROUP}; $clone->{THROWS} = $self->{THROWS}; $clone->{XMLTHROWS} = $self->{XMLTHROWS}; $clone->{UPDATED} = $self->{UPDATED}; $clone->{LINKAGESTATE} = $self->{LINKAGESTATE}; $clone->{ACCESSCONTROL} = $self->{ACCESSCONTROL}; $clone->{AVAILABILITY} = $self->{AVAILABILITY}; $clone->{LANG} = $self->{LANG}; $clone->{SUBLANG} = $self->{SUBLANG}; $clone->{SINGLEATTRIBUTES} = $self->{SINGLEATTRIBUTES}; $clone->{LONGATTRIBUTES} = $self->{LONGATTRIBUTES}; $clone->{NAMELINE_DISCUSSION} = $self->{NAMELINE_DISCUSSION}; $clone->{ATTRIBUTELISTS} = $self->{ATTRIBUTELISTS}; $clone->{APIOWNER} = $self->{APIOWNER}; $clone->{APIUID} = $self->{APIUID}; $clone->{LINKUID} = undef; # Don't ever copy this. $clone->{ORIGCLASS} = $self->{ORIGCLASS}; $clone->{ISTEMPLATE} = $self->{ISTEMPLATE}; $clone->{VALUE} = $self->{VALUE}; $clone->{RETURNTYPE} = $self->{RETURNTYPE}; my $ptref = $self->{PARSETREE}; if ($ptref) { bless($ptref, "HeaderDoc::ParseTree"); $clone->{PARSETREE} = $ptref; # ->clone(); my $pt = ${$ptref}; if ($pt) { $pt->addAPIOwner($clone); } } $clone->{TAGGEDPARAMETERS} = (); if ($self->{TAGGEDPARAMETERS}) { my @params = @{$self->{TAGGEDPARAMETERS}}; foreach my $param (@params) { my $cloneparam = $param->clone(); push(@{$clone->{TAGGEDPARAMETERS}}, $cloneparam); $cloneparam->apiOwner($clone); } } $clone->{PARSEDPARAMETERS} = (); if ($self->{PARSEDPARAMETERS}) { my @params = @{$self->{PARSEDPARAMETERS}}; foreach my $param (@params) { my $cloneparam = $param->clone(); push(@{$clone->{PARSEDPARAMETERS}}, $cloneparam); $cloneparam->apiOwner($clone); } } $clone->{VARIABLES} = (); if ($self->{VARIABLES}) { my @local_variables = @{$self->{VARIABLES}}; foreach my $local_variable (@local_variables) { my $cloned_local_variable = $local_variable->clone(); push(@{$clone->{VARIABLES}}, $cloned_local_variable); $cloned_local_variable->apiOwner($clone); } } $clone->{CONSTANTS} = (); if ($self->{CONSTANTS}) { my @params = @{$self->{CONSTANTS}}; foreach my $param (@params) { my $cloneparam = $param->clone(); push(@{$clone->{CONSTANTS}}, $cloneparam); $cloneparam->apiOwner($clone); } } $clone->{LINENUM} = $self->{LINENUM}; $clone->{CASESENSITIVE} = $self->{CASESENSITIVE}; $clone->{KEYWORDHASH} = $self->{KEYWORDHASH}; $clone->{MASTERENUM} = 0; # clones are never the master # $self->{MASTERENUM}; $clone->{APIREFSETUPDONE} = 0; $clone->{APPLEREFISDOC} = $self->{APPLEREFISDOC}; # $clone->{NOREGISTERUID} = 0; # $clone->{SUPPRESSCHILDREN} = 0; return $clone; } # /*! # @abstract # Gets/sets the contents of a simple typedef. # @param self # The (generally # {@link //apple_ref/perl/cl/HeaderDoc::Typedef Typedef}) # object. # @param contents # The value to set. (Optional.) # @discussion # This is populated by the simpleTDcontents # field in the parser state object. # */ sub typedefContents { my $self = shift; if (@_) { my $newowner = shift; $self->{TYPEDEFCONTENTS} = $newowner; } return $self->{TYPEDEFCONTENTS}; } # /*! # @abstract # Gets/sets the original class info. # @param self # The (generally Function, Method, # or Var) object. # @param origclass # The new value. (Optional.) # @discussion # When the contents of a class are explicitly included in its # subclasses using the \@superclass tag, the inherited methods # and similar get the origClass value set to the owning # class so that we don't insert an apple_ref marker for # inherited content. # */ sub origClass { my $self = shift; if (@_) { my $newowner = shift; $self->{ORIGCLASS} = $newowner; } return $self->{ORIGCLASS}; } # /*! # @abstract # The HeaderDoc class for this object. # @param self # The object. # @discussion # This is used when blessing a reference to a HeaderDoc object. # First, bless the object as a HeaderDoc::HeaderElement, # then use its CLASS field to re-bless it as # the original subclass. # */ sub class { my $self = shift; return $self->{CLASS}; } # /*! # @abstract # Returns whether the current function is a constructor or destructor. # @param self # The (generally Function) object. # @return # Returns 1 if it is a constructor or destructor, else 0. # */ sub constructor_or_destructor { my $self = shift; my $localDebug = 0; if ($self->{CLASS} eq "HeaderDoc::Function") { my $apio = $self->apiOwner(); if (!$apio) { print STDERR "MISSING API OWNER\n" if ($localDebug); return 0; } else { my $apioclass = ref($apio) || $apio; if ($apioclass ne "HeaderDoc::CPPClass") { print STDERR "Not in CPP Class\n" if ($localDebug); return 0; } } my $name = $self->rawname(); print STDERR "NAME: $name : " if ($localDebug); if ($name =~ /^~/o) { # destructor print STDERR "DESTRUCTOR\n" if ($localDebug); return 1; } $name =~ s/^\s*\w+\s*::\s*//so; # remove leading class part, if applicable $name =~ s/\s*$//so; my $classname = $apio->name(); if ($name =~ /^\Q$classname\E$/) { print STDERR "CONSTRUCTOR\n" if ($localDebug); return 1; } print STDERR "FUNCTION\n" if ($localDebug); return 0; } elsif ($self->{CLASS} eq "HeaderDoc::Method") { # @@@ DAG: If ObjC methods ever get any syntactically-special # constructors or destructors, add the checks here. return 0; } else { return 0; } } # /*! # @abstract # Gets/sets the array of local variables associated with a # function, etc. # @param self # The (generally Function) object. # @param variables # The array of variables to set. (Optional.) # */ sub variables { my $self = shift; if (@_) { @{ $self->{VARIABLES} } = @_; } # foreach my $const (@{ $self->{VARIABLES}}) {print STDERR $const->name()."\n";} ($self->{VARIABLES}) ? return @{ $self->{VARIABLES} } : return (); } # /*! # @abstract # Gets/sets the array of constants associated with an # APIOwner, Enum, etc. # @param self # The object. # @param constants # The array of constants to set. (Optional.) # */ sub constants { my $self = shift; if (@_) { @{ $self->{CONSTANTS} } = @_; } # foreach my $const (@{ $self->{CONSTANTS}}) {print STDERR $const->name()."\n";} ($self->{CONSTANTS}) ? return @{ $self->{CONSTANTS} } : return (); } # /*! # @abstract # Gets/sets whether this array is the "master" enum. # @param self # The (generally Enum) object. # @param newvalue # The value to set. (Optional.) # @discussion # This is legacy cruft that was once used for apple_ref purposes. # it is no longer used and should be removed in the future. # */ sub masterEnum { my $self = shift; if (@_) { my $masterenum = shift; $self->{MASTERENUM} = $masterenum; } return $self->{MASTERENUM}; } # /*! # @abstract # Adds a variable to the array of local variables associated with a # function, etc. # @param self # The (generally Function) object. # @param variables # An array of variables to add. # */ sub addVariable { my $self = shift; if (@_) { foreach my $item (@_) { # print "ADDED $item to $self\n"; if ($self->can("addToVars")) { $self->addToVars($item); } else { my $desc = $item->abstract(); $item->discussion($desc); $item->abstract(""); push (@{$self->{VARIABLES}}, $item); } # print "COUNT: ".$#{$self->{VARIABLES}}."\n"; } } } # /*! # @abstract # Adds a constant to the array of constants associated with an # object. # @param self # This object. # @param constants # An array of constants to add. # */ sub addConstant { my $self = shift; if (@_) { foreach my $item (@_) { push (@{$self->{CONSTANTS}}, $item); # warn("Added constant to $self\n"); } } return @{ $self->{CONSTANTS} }; } # /*! # @abstract # Adds a field to the array of fields associated with an # object. # @param self # This object. # @param fields # An array of fields to add. # */ sub addToFields { my $self = shift; if (@_) { push (@{$self->{FIELDS}}, @_); } return @{ $self->{FIELDS} }; } # /*! # @abstract # Gets/sets whether this is a template function/class. # @param self # This object. # @param istemplate # The value to set. (Optional.) # */ sub isTemplate { my $self = shift; if (@_) { $self->{ISTEMPLATE} = shift; } return $self->{ISTEMPLATE}; } # /*! # @abstract # Gets/sets whether this is a callback. # @param self # This object. # @param istemplate # The value to set. (Optional.) # */ sub isCallback { my $self = shift; if (@_) { $self->{ISCALLBACK} = shift; } if ($self->can("type")) { if ($self->type() eq "callback") { return 1; } } return $self->{ISCALLBACK}; } # /*! # @abstract # Returns whether this is an API owner (class, header, etc.) # @param self # This object. # @discussion # This is overridden by the # {@link //apple_ref/perl/cl/HeaderDoc::APIOwner APIOwner} # class. # */ sub isAPIOwner { return 0; } # /*! # @abstract # Parent discussion for inheritance # @discussion # We don't want to show this, so we can't use an # attribute. This is private. # */ sub inheritDoc { my $self = shift; if (@_) { my $inheritDoc = shift; $self->{INHERITDOC} = $inheritDoc; } return $self->{INHERITDOC}; } # /*! # @abstract # The line number where a declaration began relative to the block. # @discussion # We don't want to show this, so we can't use an # attribute. This is private. # */ sub linenuminblock { my $self = shift; if (@_) { my $linenum = shift; $self->{RAWLINENUM} = $linenum; } return $self->{RAWLINENUM}; } # /*! # @abstract # The line number of the start of the block containing a declaration. # @discussion # We don't want to show this, so we can't use an # attribute. This is private. # */ sub blockoffset { my $self = shift; # my $localDebug = 0; if (@_) { my $linenum = shift; $self->{BLOCKOFFSET} = $linenum; # cluck "For object ".$self->name()." set blockoffset to $linenum.\n" if ($localDebug); } return $self->{BLOCKOFFSET}; } # /*! # @abstract # The line number where a declaration began. # @discussion # We don't want to show this, so we can't use an # attribute. This is private. # # This uses linenuminblock and blockoffset to get the values. # Setting this attribute is no longer supported. # */ sub linenum { my $self = shift; if (@_) { my $linenum = shift; $self->{LINENUM} = $linenum; } # return $self->{LINENUM}; return $self->{RAWLINENUM} + $self->{BLOCKOFFSET}; } # /*! # @abstract # Value for constants, variables, etc. # @discussion # This is not typically shown in the HTML output, so this can't be # stored in a normal attribute. # */ sub value { my $self = shift; if (@_) { my $value = shift; $self->{VALUE} = $value; } return $self->{VALUE}; } # /*! # @abstract # Gets/sets the output format (html, xml). # @param self # This object. # @param format # The value to set. (Optional.) # */ sub outputformat { my $self = shift; if (@_) { my $outputformat = shift; $self->{OUTPUTFORMAT} = $outputformat; } else { my $o = $self->{OUTPUTFORMAT}; return $o; } } # /*! # @abstract # Gets/sets the USESTDOUT flag (0/1). # @param self # This object. # @param format # The value to set. (Optional.) # @discussion # If 0, the output is sent to files as usual. # If 1, the XML output is sent to stdout. # */ sub use_stdout { my $self = shift; if (@_) { my $use_stdout = shift; $self->{USESTDOUT} = $use_stdout; } else { my $o = $self->{USESTDOUT}; return $o; } } # /*! # @abstract # Gets/sets the function body for a function. # @param self # This object. # @param format # The value to set. (Optional.) # */ sub functionContents { my $self = shift; if (@_) { my $string = shift; $self->{FUNCTIONCONTENTS} = $string; # cluck("SET CONTENTS OF $self TO $string\n"); } return $self->{FUNCTIONCONTENTS}; } # /*! # @abstract # Gets/sets the full path to a header file. # @param self # This object. # @param path # The path to set. (Optional.) # @discussion # This contains the path as passed in on the command # line, including any leading path components. # */ sub fullpath { my $self = shift; if (@_) { my $fullpath = shift; $self->{FULLPATH} = $fullpath; } else { my $n = $self->{FULLPATH}; return $n; } } # /*! # @abstract # Gets/sets the filename for a header file. # @param self # This object. # @param filename # The filename to set. (Optional.) # @discussion # This contains the filename as passed in on the command # line, with any leading path components stripped off. # */ sub filename { my $self = shift; if (@_) { my $filename = shift; $self->{FILENAME} = $filename; } else { my $n = $self->{FILENAME}; return $n; } } # /*! # @abstract # Gets/sets the name of the first constant in an enumeration. # @param self # This object. # @param firstconstname # The value to set. (Optional.) # @discussion # If no name could be determiend for an enumeration, this value is # used. # */ sub firstconstname { my $self = shift; my $localDebug = 0; my($class) = ref($self) || $self; print STDERR "$class\n" if ($localDebug); if (@_) { my $name = shift; print STDERR "Set FIRSTCONSTNAME to $name\n" if ($localDebug); $self->{FIRSTCONSTNAME} = $name; } return $self->{FIRSTCONSTNAME}; } # /*! # @abstract # Gets/sets the name of this function/var/class/*. # @param self # This object. # @param name # The value to set. (Optional.) # @discussion # This function returns the name as taken from the # HeaderDoc comment. If the nameline contains multiple # words and there are additional nonempty discussion # lines, the entire name line is treated as the # name, and the nameline discussion is appended to # the value returned by {@link mediumrarename} to # obtain the value that this function returns. # # An explicit \@discussion tag forces the # nameline discussion to be treated as part of the name # even if the contents of the discussion tag are empty. # # This may be a separate value from the rawname # and rawname_extended values. # */ sub name { my $self = shift; my $localDebug = 0; # cluck("namebacktrace\n"); my($class) = ref($self) || $self; print STDERR "IN NAME: $class\n" if ($localDebug); if (@_) { my $name = shift; # cluck("name set to $name\n"); my $oldname = $self->{NAME}; # cluck("namebacktrace: set to \"$name\", prev was \"$oldname\".\n"); my $fullpath = $self->fullpath(); my $linenum = $self->linenum(); my $class = ref($self) || $self; print STDERR "NAMESET: $self -> $name\n" if ($localDebug); if (!($class eq "HeaderDoc::Header") && ($oldname && length($oldname)) && !length($self->{FORCENAME})) { # Don't warn for headers, as they always change if you add # text after @header. Also, don't warn if the new name # contains the entire old name, to avoid warnings for # multiword names. Otherwise, warn the user because somebody # probably put multiple @function tags in the same comment # block or similar.... my $nsoname = $oldname; my $nsname = $name; if ($class =~ /^HeaderDoc::ObjC/) { $nsname =~ s/\s//g; $nsoname =~ s/\s//g; } elsif ($class =~ /^HeaderDoc::Method/) { $nsname =~ s/:$//g; $nsoname =~ s/:$//g; # } else { # warn("CLASS: $class\n"); } if ($nsname !~ /\Q$nsoname\E/) { if (!$HeaderDoc::ignore_apiuid_errors) { warn("$fullpath:$linenum: warning: Name being changed ($oldname -> $name)\n"); } } elsif (($class eq "HeaderDoc::CPPClass" || $class =~ /^HeaderDoc::ObjC/o) && $name =~ /:/o) { warn("$fullpath:$linenum: warning: Class name contains colon, which is probably not what you want.\n"); } } elsif (length($self->{FORCENAME})) { $name = $self->{FORCENAME}; } $name =~ s/\n$//sgo; $name =~ s/\s$//sgo; $self->{NAME} = $name; } my $n = $self->{NAME}; # Append the rest of the name line if necessary. if ($self->{DISCUSSION_SET}) { # print STDERR "DISCUSSION IS: ".$self->{DISCUSSION}."\n"; # print STDERR "ISAPIO: ".$self->isAPIOwner()."\n"; # print STDERR "ISFW: ".$self->isFramework()."\n"; if ((!$HeaderDoc::ignore_apiowner_names) || (!$self->isAPIOwner() && ($HeaderDoc::ignore_apiowner_names != 2)) || $self->isFramework()) { print STDERR "NAMELINE DISCUSSION for $self CONCATENATED (".$self->{NAMELINE_DISCUSSION}.")\n" if ($localDebug); print STDERR "ORIGINAL NAME WAS \"$n\"\n" if ($localDebug); if (length($self->{NAMELINE_DISCUSSION})) { $n .= " ".$self->{NAMELINE_DISCUSSION}; } } } if ($class eq "HeaderDoc::Function" && $self->conflict()) { $n .= "("; $n .= $self->getParamSignature(1); $n .= ")"; } # If there's nothing to return, try returning the first constant in the case of enums. if ($n !~ /\S/) { $n = $self->firstconstname(); } return $n; } # /*! # @abstract # Gets/sets the SEEALSODUPCHECK value. # @param self # This object. # @param name # The value to set. (Optional.) # @discussion # This is used when doing cross-linking between # multiple objects (in {@link blockParseOutside}) # to ensure that duplicate entries are not inserted. # */ sub seeDupCheck { my $self = shift; my $name = shift; my $set = 0; if (@_) { $set = shift; } my %dupcheck = (); if ($self->{SEEALSODUPCHECK}) { %dupcheck = %{$self->{SEEALSODUPCHECK}}; } my $retval = $dupcheck{$name}; if ($set) { $dupcheck{$name} = $name; } $self->{SEEALSODUPCHECK} = \%dupcheck; return $retval; } # /*! # @abstract # Add see/seealso (JavaDoc compatibility enhancement) # */ sub see { my $self = shift; my $liststring = shift; my $type = "See"; my $apiUIDPrefix = HeaderDoc::APIOwner->apiUIDPrefix(); my $dupDebug = 0; print STDERR "see called\n" if ($dupDebug); # $liststring =~ s/
/\n/sg; # WARNING \n would break attributelist! # Is it a see or seealso? if ($liststring =~ s/^seealso\s+//iso) { $type = "See Also"; } else { $liststring =~ s/^see\s+//iso; } # $liststring =~ s/(\n|\r|\|\s)+$//sgi; my @items = split(/[\n\r]/, $liststring); # print STDERR "LS: $liststring\n"; foreach my $item (@items) { if ($item !~ /^\/\/\w+\// && $item =~ /\S/) { ## API anchor (apple_ref or other) # print STDERR "NO API ANCHOR: $item\n"; my @list = split(/\s+/, $item, 2); # Generate it with its own name as the title. We're just going # to rip it back apart anyway. my $see = $list[0]; my $name = $list[1]; # my $apiref = $self->genRefHTML("", $see, $see); if ($name !~ /\S/) { $name = $see; } # print STDERR "NAME: $name SEE: $see\n"; print STDERR "Checking \"$see\" for duplicates.\n" if ($dupDebug); if (!$self->seeDupCheck($see)) { # my $apiuid = $apiref; # $name =~ s/\s+/ /sg; # $name =~ s/\s+$//sg; # $apiuid =~ s/^.*\s*.*?\s*//s; # print STDERR "APIREF: $apiref\n"; # print STDERR "NAME: $name APIUID: $apiuid\n"; my $apiuid = $see; $self->attributelist($type, $name."\n$apiuid"); $self->seeDupCheck($see, 1); print STDERR "Not found. Adding.\n" if ($dupDebug); } else { print STDERR "Omitting duplicate \"$see\"[1]\n" if ($dupDebug); } } elsif ($item =~ /\S/) { # print STDERR "API ANCHOR: $item\n"; $item =~ s/^\s*//s; $item =~ s/\s+/ /sg; my @parts = split(/\s+/, $item, 2); my $name = $parts[1]; $name =~ s/\s+$//sg; print STDERR "Checking \"$name\" for duplicates.\n" if ($dupDebug); if (!$self->seeDupCheck($name)) { # print STDERR "$type -> '".$name."' -> '".$parts[0]."'\n"; $self->attributelist($type, $name."\n".$parts[0]); $self->seeDupCheck($name, 1); print STDERR "Not found. Adding.\n" if ($dupDebug); } else { print STDERR "Omitting duplicate \"$name\"[2]\n" if ($dupDebug); } } } } # /*! # @abstract # Returns the "medium rare" name. # @param self # This object. # @discussion # Returns the value of the name from the HeaderDoc comment without # appending the nameline discussion (ever). See # {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/name//() name} # for more explanation. # */ sub mediumrarename { my $self = shift; return $self->{NAME}; } # /*! # @abstract # Returns the "raw" name. # @param self # This object. # @discussion # Returns the value of the name from the HeaderDoc comment without further # processing. # # If no "raw" name was set, returns the main name (from {@link mediumrarename}. # */ sub rawname { my $self = shift; my $localDebug = 0; if (@_) { my $name = shift; $self->{RAWNAME} = $name; print STDERR "RAWNAME: $name\n" if ($localDebug); } my $n = $self->{RAWNAME}; if (!($n) || !length($n)) { $n = $self->{NAME}; } return $n; } # /*! # @abstract # Clears the group for this object. # @param self # This object. # */ sub clearGroup { my $self = shift; $self->{GROUP} = undef; } # /*! # @abstract # Gets/sets the group for this object and adds it to the group object. # @param self # This object. # @param group # The group to set for this object. (Optional.) # */ sub group { my $self = shift; if (@_) { my $group = shift; $self->{GROUP} = $group; if (!$self->isAPIOwner()) { # cluck("SELF: $self\nAPIO: ".$self->apiOwner()."\n"); # $self->dbprint(); # $self->apiOwner()->addGroup($group); $self->apiOwner()->addToGroup($group, $self); } } else { my $n = $self->{GROUP}; return $n; } } # /*! # @abstract # This function adds an attribute for a class or header. # @param name # The name of the attribute to be added # @param attribute # The contents of the attribute # @param long # 0 for single line, 1 for multi-line. # */ sub attribute { my $self = shift; my $name = shift; my $attribute = shift; my $long = shift; my $programmatic = 0; if (@_) { $programmatic = shift; } my $localDebug = 0; cluck("Attribute added:\nNAME => $name\nATTRIBUTE => $attribute\nLONG => $long\nPROGRAMMATIC => $programmatic\n") if ($localDebug); my %attlist = (); if ($long) { if ($self->{LONGATTRIBUTES}) { %attlist = %{$self->{LONGATTRIBUTES}}; } } else { if ($self->{SINGLEATTRIBUTES}) { %attlist = %{$self->{SINGLEATTRIBUTES}}; } $attribute =~ s/\n/ /sgo; $attribute =~ s/^\s*//so; $attribute =~ s/\s*$//so; } if ($programmatic || !$long) { $attlist{$name}=$attribute; } else { $attlist{$name}=filterHeaderDocTagContents($attribute); } # print STDERR "AL{$name}: ".$attlist{$name}."\n"; if ($long) { $self->{LONGATTRIBUTES} = \%attlist; } else { $self->{SINGLEATTRIBUTES} = \%attlist; } my $temp = $self->getAttributes(2); print STDERR "Attributes: $temp\n" if ($localDebug); } # /*! # @abstract # Returns the attributes for this object, formatted in HTML # for output. # @param self # This HeaderDoc API object. # @param long # Pass 0 for short attributes by themselves, 1 for long # attributes by themselves, or 2 for both short and long # attributes. # */ sub getAttributes { my $self = shift; my $long = shift; my %attlist = (); my $localDebug = 0; my $xml = 0; my $newTOC = $HeaderDoc::newTOC; my $class = ref($self) || $self; my $uid = $self->apiuid(); # Only use this style for API Owners. if (!$self->isAPIOwner()) { $newTOC = 0; } my $apiowner = $self->apiOwner(); if ($apiowner->outputformat() eq "hdxml") { $xml = 1; } my $first = 1; # my $declaredin = $self->declaredIn(); # print STDERR "DECLARED IN: $declaredin\n"; my $maybe = 0; my $retval = ""; if ($long != 1) { if ($self->{SINGLEATTRIBUTES}) { %attlist = %{$self->{SINGLEATTRIBUTES}}; } foreach my $key (sort strcasecmp keys %attlist) { my $keyname = $key; # printed name. if ($key eq "Superclass" && ($HeaderDoc::superclassName =~ /\S/)) { $keyname = $HeaderDoc::superclassName; } print STDERR "KEY NAME CHANGED TO \"$keyname\"\n" if ($localDebug); my $value = $attlist{$key}; my $newatt = $value; if (($key eq "Superclass" || $key eq "Extends Class" || $key eq "Extends Protocol" || $key eq "Conforms to" || $key eq "Implements Class")) { my $classtype = "class"; my $selfclass = ref($self) || $self; if ($selfclass =~ /HeaderDoc::ObjC/) { if ($key eq "Conforms to" || $key eq "Extends Protocol") { $classtype = "protocol"; } } my @valparts = split(/\cA/, $value); $newatt = ""; # print STDERR "CLASSTYPE: $classtype\n"; foreach my $valpart (@valparts) { # print STDERR "VALPART: $valpart\n"; if (length($valpart)) { $valpart =~ s/^\s*//s; if ($valpart =~ /^(\w+)(\W.*)$/) { # print STDERR "1: $1 2: $2\n"; $newatt .= $self->genRef("$classtype", $1, $1).$self->textToXML($2); } else { $newatt .= $self->genRef("$classtype", $valpart, $self->htmlToXML($valpart)); } if (!$xml) { $newatt .= ", "; } } } $newatt =~ s/, $//s; if ($xml) { $keyname =~ s/ / /sg; } } elsif ($key eq "Path To Headers" && !$xml) { $newatt = "\n$value\n\n"; } elsif ($key eq "Framework Path" && !$xml) { $newatt = "\n$value\n\n"; } elsif ($key eq "Requested Copyright" && !$xml) { $newatt = "\n$value\n\n"; } elsif ($key eq "Requested UID" && !$xml) { $newatt = "\n$value\n\n"; } elsif ($xml) { $keyname = $self->htmlToXML($keyname); $newatt = $self->htmlToXML($newatt); } else { print STDERR "KEY: $key\n" if ($localDebug); } if ($xml) { $retval .= "$keyname$newatt\n"; } else { if ($newTOC) { if ($first) { $retval .= "
\n"; $first = 0; } $retval .= "\n"; } else { $retval .= "

$keyname

\n\n

$newatt

\n"; } } } my $declaredin = $self->declaredIn(); if ($declaredin && ( # $HeaderDoc::enable_custom_references || $self->isAPIOwner())) { my $blockclass = "\$\$customref_show_block\@\@"; my $rowclass = " class=\"customref_show_row\""; if ($self->isAPIOwner()) { $blockclass = ""; $rowclass = ""; } if (!$xml) { if ($newTOC) { if ($first) { $retval .= "
$keyname:
$newatt
\n"; $first = 0; $maybe = 1; } $retval .= "\n"; } else { $retval .= "

Declared In

$declaredin

\n"; } } # cluck("Backtrace\n"); # warn "DECLAREDIN: $declaredin\n"; # warn "RV: $retval\n"; } } if ($long != 0) { if ($self->{LONGATTRIBUTES}) { %attlist = %{$self->{LONGATTRIBUTES}}; } foreach my $key (sort strcasecmp keys %attlist) { my $value = $attlist{$key}; if ($xml) { $key = $self->htmlToXML($key); $value = $self->htmlToXML($value); $retval .= "$key$value\n"; } else { $maybe = 0; if ($newTOC) { if ($first) { $retval .= "
Declared In:
$declaredin
\n"; $first = 0; } $retval .= "\n"; } else { $retval .= "

$key

\n\n

$value

\n"; } } } } if ((!$xml) && ($newTOC && !$first)) { $retval .= "
$key:
$value
\n"; } # elsif (!$newTOC) { $retval = "

$retval

"; } # libxml parser quirk workaround # if ((!$xml) && $HeaderDoc::enable_custom_references) { # # This only gets inserted if the entire spec box should go away. # if ($maybe) { # $retval =~ s/\$\$customref_show_block\@\@/customref_show_block /sg; # } else { # $retval =~ s/\$\$customref_show_block\@\@//sg; # } # } return $retval; } # /*! # @abstract # Returns the value of an attribute in # the object's long or short attributes whose # name matches the one passed in. # @param self # This object. # @param name # The name to look for. # */ sub checkShortLongAttributes { my $self = shift; my $name = shift; my $localDebug = 0; my %singleatts = (); if ($self->{SINGLEATTRIBUTES}) { %singleatts = %{$self->{SINGLEATTRIBUTES}}; } my %longatts = (); if ($self->{LONGATTRIBUTES}) { %longatts = %{$self->{LONGATTRIBUTES}}; } my $value = $singleatts{$name}; if ($value && length($value)) {return $value;} $value = $longatts{$name}; if ($value && length($value)) {return $value;} return 0; } # /*! # @abstract # Returns the value of an attribute list whose # name matches the one passed in. # @param self # This object. # @param name # The name to look for. # */ sub checkAttributeLists { my $self = shift; my $name = shift; my $localDebug = 0; my %attlists = (); if ($self->{ATTRIBUTELISTS}) { %attlists = %{$self->{ATTRIBUTELISTS}}; } # print STDERR "list\n"; my $retval = ""; my $value = $attlists{$name}; if ($value) { return $value; } return 0; } # /*! # @abstract # Returns the attribute lists for this object. # @param self # This object. # @param composite # Pass 1 to obtain the list for the composite page, # else 0. Used for generating API references for # any #define declarations in define blocks. # */ sub getAttributeLists { my $self = shift; my $composite = shift; my $localDebug = 0; my $xml = 0; my $newTOC = $HeaderDoc::newTOC; my $uid = $self->apiuid(); my $isFramework = 0; if ($self->can('isFramework') && $self->isFramework()) { $isFramework = 1; } # Only use this style for API Owners. if (!$self->isAPIOwner()) { $newTOC = 0; } my $apiowner = $self->apiOwner(); if (!$apiowner) { cluck("No api owner for $self\n"); } if ($apiowner->outputformat() eq "hdxml") { $xml = 1; } my %attlists = (); if ($self->{ATTRIBUTELISTS}) { %attlists = %{$self->{ATTRIBUTELISTS}}; } # print STDERR "list\n"; my $retval = ""; my $first = 1; # print STDERR "START OF SORTED LIST\n"; foreach my $key (sort strcasecmp (keys %attlists)) { # print STDERR "KEY: $key\n"; my $prefix = ""; my $suffix = ""; if ($isFramework && ($key eq "See" || $key eq "See Also")) { $prefix = "\n"; $suffix = "\n\n"; } if ($xml) { $retval .= "$key\n"; } else { $retval .= $prefix; if ($newTOC) { if ($first) { $retval .= "
\n"; $first = 0; } $retval .= "\n"; } else { $retval .= "\n"; } $retval .= $suffix; } } # print STDERR "END OF SORTED LIST\n"; if ($newTOC) { if (!$first) { $retval .= "
$key:
\n"; } else { $retval .= "

$key

\n"; } } print STDERR "key $key\n" if ($localDebug); my @list = @{$attlists{$key}}; foreach my $item (@list) { if ($item !~ /\S/s) { next; } print STDERR "item: $item\n" if ($localDebug); my ($name, $disc, $namedisc) = &getAPINameAndDisc($item, $self->lang()); if ($key eq "Included Defines") { # @@@ CHECK SIGNATURE my $apiref = $self->apiref($composite, "macro", $name); $name .= "$apiref"; } if ($xml) { $name = $self->htmlToXML($name); $disc = $self->htmlToXML($disc); } if (($key eq "See Also" || $key eq "See")) { $name =~ s/^(\s|
|

)+//sgio; $name =~ s/(\s|
|<\/p>)+$//sgio; $disc =~ s/^(\s|
|

)+//sgio; $disc =~ s/(\s|
|<\/p>)+$//sgio; $name =~ s/\cD/ /sgo; # print STDERR "DISC: $disc\n"; if ($xml) { $disc = "$name"; } else { $name = "

$name

"; $disc = ""; } } if ($xml) { $retval .= "$name$disc"; } else { $retval .= "
$name
$disc
"; } } if ($xml) { $retval .= "\n"; } else { if ($newTOC) { $retval .= "
\n"; } } # print STDERR "done\n"; return $retval; } # /*! # @abstract # Add an attribute list. # @param name # The name of the list # @param attribute # A string in the form "term description..." # containing a term and description to be inserted # into the list named by name. # */ sub attributelist { my $self = shift; my $name = shift; my $attribute = shift; my %attlists = (); # cluck "Add attributelist MAPPING $name -> $attribute\n"; if ($self->{ATTRIBUTELISTS}) { %attlists = %{$self->{ATTRIBUTELISTS}}; } my @list = (); my $listref = $attlists{$name}; if ($listref) { @list = @{$listref}; } push(@list, filterHeaderDocTagContents($attribute)); $attlists{$name}=\@list; $self->{ATTRIBUTELISTS} = \%attlists; # print STDERR "AL = $self->{ATTRIBUTELISTS}\n"; # print STDERR $self->getAttributeLists()."\n"; } # /*! # @abstract # Gets/sets the API owner for this object. # @param self # This object. # @param owner # The {@link //apple_ref/perl/cl/HeaderDoc::APIOwner APIOwner} object # to set as this object's API owner. # */ sub apiOwner { my $self = shift; if (@_) { my $temp = shift; $self->{APIOWNER} = $temp; } return $self->{APIOWNER}; } # use Devel::Peek; # /*! # @abstract # Generates the API ref (apple_ref) for a function, data type, etc. # @param self # This object. # @param composite # Set to 1 for the composite page (if classAsComposite is # not 1), else 0. # */ sub apiref { my $self = shift; # print STDERR "IY0\n"; Dump($self); my $filename = $self->filename(); my $linenum = $self->linenum(); my $composite = shift; my $args = 0; my $type = ""; my $apiowner = $self->apiOwner(); # print STDERR "IY0a\n"; Dump($self); my $owningclass = ref($apiowner) || $self; my $paramSignature = ""; # print STDERR "IY1\n"; Dump($self); if (@_) { $args = 1; $type = shift; if (@_) { $paramSignature = shift; } } else { my $uid = $self->apiuid(); if ($uid =~ /\/\/.*?\/.*?\/(.*?)\//o) { $type = $1; } } # print STDERR "DEBUG: SELF: $self NAME: ".$self->name()." FILENAME: $filename LINENUM: $linenum\n"; # print STDERR "DEBUG: COMPOSITE: $composite PARAMSIGNATURE: $paramSignature TYPE: $type\n"; # Don't provide API refs for inherited data or functions. my $forceuid = ""; if ($self->origClass() ne "") { $forceuid = $self->generateLinkUID($composite); } # we sanitize things now. # if ($paramSignature =~ /[ <>\s\n\r]/o) { # my $fullpath = $self->fullpath(); # warn("$fullpath:$linenum:apiref: bad signature \"$paramSignature\". Dropping ref.\n"); # return ""; # } # print STDERR "IY3\n"; Dump($self); my $uid = ""; if ($args && !$forceuid) { # Do this first to assign a UID, even if we're doing the composite page. $uid = $self->apiuid($type, $paramSignature); } else { $uid = $self->apiuid(); } # print STDERR "IY4\n"; Dump($self); # print STDERR "COMPO: $composite CAC: ".$HeaderDoc::ClassAsComposite."\n"; if ($composite && !$HeaderDoc::ClassAsComposite) { $uid = $self->compositePageUID(); } elsif (!$composite && $HeaderDoc::ClassAsComposite) { # The composite page is the master, so give the individual # pages composite page UIDs. These never get generated # anyway. $uid = $self->compositePageUID(); } if ($forceuid) { $uid = $forceuid; } # print STDERR "IY5\n"; Dump($self); my $ret = ""; if (length($uid)) { my $name = $self->name(); if ($self->can("rawname")) { if (!$self->{DISCUSSION} || !$self->{NAMELINE_DISCUSSION}) { $name = $self->rawname(); } } my $extendedname = $name; # print STDERR "NAME: ".$self->name()."\n"; # print STDERR "RAWNAME: ".$self->rawname()."\n"; if ($owningclass ne "HeaderDoc::Header" && $self->sublang() ne "C") { # Don't do this for COM interfaces and C pseudoclasses $extendedname = $apiowner->rawname() . "::" . $name; } # $extendedname =~ s/\s//sgo; $extendedname =~ s/<.*?>//sgo; $extendedname =~ s/;//sgo; my $uidstring = ""; my $indexgroup = $self->indexgroup(); if (length($uid)) { $uidstring = " uid=$uid; "; } if (length($indexgroup)) { $uidstring .= " indexgroup=$indexgroup; "; } # if ($type eq "") { # cluck("empty type field\n"); # } my $fwshortname = ""; if ($self->isFramework()) { $fwshortname = $self->filename(); $fwshortname =~ s/\.hdoc$//so; $fwshortname = sanitize($fwshortname, 1); $fwshortname = "shortname=$fwshortname;"; } $ret .= "\n"; # Don't add the actual anchor for a framework UID because it would # conflict with the page generated by gatherHeaderDoc. if (length($uid) && !$self->isFramework()) { $ret .= "\n"; } } # print STDERR "IY8\n"; Dump($self); $owningclass = undef; # print STDERR "IY9\n"; Dump($self); # print STDERR "APIREF: $ret\n"; return $ret; } # /*! # @abstract # Sets the index group for a class or header. # @discussion # This allows grouping of headers and/or classes in the main TOC generated by # {@link //apple_ref/doc/header/gatherHeaderDoc.pl gatherHeaderDoc} # It is unrelated to groupings of functions, etc. within a header. # */ sub indexgroup { my $self = shift; if (@_) { my $group = shift; $group =~ s/^\s*//sg; $group =~ s/\s*$//sg; $group =~ s/;/\\;/sg; $group .= " "; $self->{INDEXGROUP} = $group; } my $ret = $self->{INDEXGROUP}; if (!length($ret)) { my $apio = $self->apiOwner(); if ($apio && ($apio != $self)) { return $apio->indexgroup(); } } return $ret; } # /*! # @abstract # Generates a special UID for inherited content # @param self # This object. # @param composite # Set to 1 if generating for a composite page (if # classAsComposite is not 1), else 0. # */ sub generateLinkUID { my $self = shift; my $composite = shift; if ($self->{LINKUID}) { # print STDERR "LINKUID WAS ".$self->{LINKUID}."\n"; if ($composite) { return $self->compositePageUID(); } return $self->{LINKUID}; } my $classname = sanitize($self->apiOwner()->rawname(), 1); my $name = sanitize($self->rawname(), 1); my $apiUIDPrefix = HeaderDoc::APIOwner->apiUIDPrefix(); my $uniquenumber = $HeaderDoc::uniquenumber++; my $uid = "//$apiUIDPrefix/doc/inheritedContent/$classname/$name/$uniquenumber"; $self->{LINKUID} = $uid; # registerUID($uid); if ($composite) { return $self->compositePageUID() } return $uid; } # /*! # @abstract # Returns the name of an API element for UID purposes. # */ sub apiuidname { my $self = shift; my $class = ref($self) || $self; my $localDebug = 0; my $name = $self->mediumrarename(); if (!($self->can("conflict")) || ($self->can("conflict") && !($self->conflict()))) { print STDERR "No conflict.\n" if ($localDebug); $name = $self->rawname(); if ($class eq "HeaderDoc::ObjCCategory") { # Category names are in the form "ClassName(DelegateName)" if ($name =~ /\s*\w+\s*\(.+\).*/o) { $name =~ s/\s*(\w+)\s*\(\s*(\w+)\s*\).*/$1($2)/o; } } # Silently drop leading and trailing space $name =~ s/^\s*//so; $name =~ s/\s*$//so; # Don't silently drop spaces. # We sanitize things now. # $name =~ s/\s//sgo; # $name =~ s/<.*?>//sgo; # if ($name =~ /[ \(\)<>\s\n\r]/o) { # if (!$HeaderDoc::ignore_apiuid_errors) { # my $fullpath = $self->fullpath(); # warn("$fullpath:$linenum:apiref: bad name \"$name\". Dropping ref.\n"); # } # return ""; # } print STDERR "Sanitized name: $name\n" if ($localDebug); } else { print STDERR "Conflict detected.\n" if ($localDebug); my $apiOwner = $self->apiOwner(); my $apiOwnerClass = ref($apiOwner) || $apiOwner; if ($apiOwnerClass eq "HeaderDoc::CPPClass") { $name = $self->rawname(); } else { $name =~ s/ //sgo; } # Silently drop leading and trailing space $name =~ s/^\s*//so; $name =~ s/\s*$//so; # Don't silently drop spaces. # We sanitize things now. # $name =~ s/\s//sgo; # $name =~ s/<.*?>//sgo; # if ($name =~ /[\s\n\r]/o) { # if (!$HeaderDoc::ignore_apiuid_errors) { # my $fullpath = $self->fullpath(); # warn("$fullpath:$linenum:apiref: bad name \"$name\". Dropping ref.\n"); # } # return ""; # } } $name =~ s/\n//smgo; if ($name =~ /^operator\s+\w/) { $name =~ s/^operator\s+/operator_/; } else { $name =~ s/^operator\s+/operator/; } return $name; } # /*! # @abstract # Sets the apple_ref ID for a data type, function, etc. # @param obj # The object for which you want ot generate an API ref. # @discussion # The actual API ref eneration occurs in {@link apirefSetup}. # */ sub apiuid { my $self = shift; my $type = "AUTO"; my $paramSignature_or_alt_define_name = ""; my $filename = $self->filename(); my $linenum = $self->linenum(); my $localDebug = 0; # NOTE: localDebug is reset to 0 below. print STDERR "IN apiuid\n" if ($localDebug); if (@_) { $type = shift; if (@_) { $paramSignature_or_alt_define_name = shift; } } else { if ($self->{LINKUID}) { print STDERR "RETURNING CACHED LINKUID ".$self->{LINKUID}."\n" if ($localDebug); return $self->{LINKUID}; } # if (!$self->{APIUID}) { # $self->apirefSetup(1); # } # if ($self->{APIUID}) { print STDERR "RETURNING CACHED APIUID ".$self->{APIUID}."\n" if ($localDebug); return $self->{APIUID}; # } } if ($self->{REQUESTEDUID} && length($self->{REQUESTEDUID})) { $self->{APIUID} = $self->{REQUESTEDUID}; print STDERR "RETURNING EXPLICITLY REQUESTED APIUID ".$self->{REQUESTEDUID}."\n" if ($localDebug); return $self->{REQUESTEDUID}; } print STDERR "GENERATING NEW APIUID FOR $self (".$self->name.")\n" if ($localDebug); my $olduid = $self->{APIUID}; if ($self->{LINKUID}) { $olduid = $self->{LINKUID}; } my $name = $self->apiuidname(); $localDebug = 0; my $className; my $lang = $self->sublang(); my $class = ref($self) || $self; cluck("Call trace\n") if ($localDebug); my $parentClass = $self->apiOwner(); if ($type eq "econst") { while (!$parentClass->isAPIOwner()) { $parentClass = $parentClass->apiOwner(); } } # print STDERR "OBJ: $self PC: $parentClass\n" if ($HeaderDoc::debugAllocations); my $parentClassType = ref($parentClass) || $parentClass; if ($parentClassType eq "HeaderDoc::Header") { # Generate requests with sublang always (so that, for # example, a c++ header can link to a class from within # a typedef declaration. # Generate anchors (except for class anchors) with lang # if the parent is a header, else sublang for stuff # within class braces so that you won't get name # resolution conflicts if something in a class has the # same name as a generic C entity, for example. if (!($class eq "HeaderDoc::CPPClass" || $class =~ /^HeaderDoc::ObjC/o)) { # print STDERR "LANG $lang\n"; if ($lang ne "IDL" && $lang ne "MIG" && $lang ne "javascript") { $lang = $self->lang(); } } } $lang = $self->apiRefLanguage($lang); # if ($lang eq "MIG") { # $lang = "mig"; # } elsif ($lang eq "IDL") { # $lang = $HeaderDoc::idl_language; # } # if ($lang eq "C") { $lang = "c"; } # if ($lang eq "Csource") { $lang = "c"; } # if ($lang eq "occCat") { $lang = "occ"; } # if ($lang eq "intf") { $lang = "occ"; } # print STDERR "SUBLANG: $lang\n"; # my $lang = "c"; # my $class = ref($HeaderDoc::APIOwner) || $HeaderDoc::APIOwner; # if ($class =~ /^HeaderDoc::CPPClass$/o) { # $lang = "cpp"; # } elsif ($class =~ /^HeaderDoc::ObjC/o) { # $lang = "occ"; # } print STDERR "LANG: $lang\n" if ($localDebug); # my $classHeaderObject = HeaderDoc::APIOwner->headerObject(); # if (!$classHeaderObject) { } if ($parentClassType eq "HeaderDoc::Header") { # We're not in a class. We used to give the file name here. if (!$HeaderDoc::headerObject) { die "headerObject undefined!\n"; } # All static/my variables are file-scoped and shuld not cause # an API ref conflict with other file-scoped variables # with the same name. if ($self->parserState() && $self->parserState()->{isStatic}) { $className = $HeaderDoc::headerObject->filename(); if (!(length($className))) { die "Header Name empty!\n"; } # $className .= "/"; } else { $className = ""; } } else { # We're in a class. Give the class name. # cluck("PC: $parentClass\n"); # $self->dbprint(); $className = $parentClass->name(); # if (length($name)) { $className .= "/"; } } $className =~ s/\s//sgo; $className =~ s/<.*?>//sgo; # print STDERR "CN: $className\n"; # Macros are not part of a class in any way. $class = ref($self) || $self; if ($class eq "HeaderDoc::PDefine") { $className = ""; if ($paramSignature_or_alt_define_name) { $name = $paramSignature_or_alt_define_name; $paramSignature_or_alt_define_name = ""; } } if ($class eq "HeaderDoc::Header") { # Headers are a "doc" reference type. $className = ""; $lang = "doc"; if ($self->isFramework()) { $type = "framework"; } else { $type = "header"; } $name = $self->filename(); if ($self->can('isFramework') && $self->isFramework()) { $name =~ s/\.hdoc$//s; } } my $apio = $self->apiOwner(); my $sublang = $self->sublang(); if ($type eq "intfm" && ($sublang eq "c" || $sublang eq "C") && $apio =~ /HeaderDoc::CPPClass/) { $lang = "doc/com"; $type = "intfm"; } if ($self->appleRefIsDoc()) { $lang = "doc"; $type = "title:$type"; $name = $self->rawname_extended(); } print STDERR "genRefSub: \"$lang\" \"$type\" \"$name\" \"$className\" \"$paramSignature_or_alt_define_name\"\n" if ($localDebug); my $uid = $self->genRefSub($lang, $type, $name, $className, $paramSignature_or_alt_define_name); if (length($name)) { unregisterUID($olduid, $name, $self); $uid = registerUID($uid, $name, $self); # This call resolves conflicts where needed.... } print STDERR "APIUID SET TO $uid\n" if ($localDebug); $self->{APIUID} = $uid; return $uid; # my $ret .= "\n"; # return $ret; } # /*! # @param value # @abstract # Sets or gets a state flag. # @discussion # The APPLEREFISDOC state flag controls whether to use a # language-specific or doc-specific apple_ref marker for a doc block. # */ sub appleRefIsDoc { my $self = shift; if (@_) { my $value = shift; $self->{APPLEREFISDOC} = $value; } # print STDERR "ARID: ".$self->{APPLEREFISDOC}." for $self\n"; return $self->{APPLEREFISDOC}; } # /*! # @abstract # Constructs an API reference from its constituent parts. # @param self # This HeaderDoc API object. # @param lang # The programming language. # @param type # The symbol type. # @param name # The symbol name. # @param className # The class containing the symbol (with a trailing slash appended). # Pass an empty string if the symbol is not a class member. # @param paramSignature # The parameter signature for a function or method (prefixed # with a leading slash, with parentheses around the actual # signature. (Optional.) # */ sub genRefSub($$$$) { my $self = shift; my $orig_lang = shift; my $orig_type = shift; my $orig_name = shift; my $orig_className = shift; my $orig_paramSignature = ""; if (@_) { $orig_paramSignature = shift; } my $owner_class = ""; if (@_) { $owner_class = shift; } my $lang = sanitize($orig_lang); my $type = sanitize($orig_type); my $name = sanitize($orig_name, 1); my $className = sanitize($orig_className, 1); my $paramSignature = sanitize($orig_paramSignature); if (length($className) && length($name)) { $className .= "/"; } my $apiUIDPrefix = HeaderDoc::APIOwner->apiUIDPrefix(); my $localDebug = 0; $lang = $self->apiRefLanguage($lang); if ($lang eq "perl") { # You can declare arbitrary variables in Perl inside a # package that are actually outside the package with # this syntax, e.g. $otherPackage::variable = "blah" # Don't do this for classes; they're not scoped. # No need to filter out headers here because they have # "doc" as the language. # print "NAME: $name CN: $className\n"; if ($type ne "cl") { if ($name =~ s/^(.+)\:\://s) { $className = "$1/"; } } # print "NEW NAME: $name CN: $className\n"; } my $uid; if (length($owner_class)) { $uid = "//$apiUIDPrefix/$lang/$type/$owner_class/$className$name$paramSignature"; } else { $uid = "//$apiUIDPrefix/$lang/$type/$className$name$paramSignature"; } return $uid; } # /*! # @abstract # Returns the language token that should appear in an # API reference. # @param self # The current object. # @param lang # The raw HeaderDoc (internal) language name. # */ sub apiRefLanguage { my $self = shift; my $lang = shift; if ($lang eq "javascript") { $lang = "js"; } if ($lang eq "csh") { $lang = "shell"; } if ($lang eq "MIG") { $lang = "mig"; } elsif ($lang eq "IDL") { $lang = $HeaderDoc::idl_language; } if ($lang eq "C") { $lang = "c"; } if ($lang eq "Csource") { $lang = "c"; } if ($lang eq "occCat") { $lang = "occ"; } if ($lang eq "intf") { $lang = "occ"; } return $lang; } # /*! # @abstract # Gets/sets the list of exceptions thrown by this # class/function. # @param self # The current object. # @param throws # The new value. (Optional.) # */ sub throws { my $self = shift; my $newTOC = $HeaderDoc::newTOC; if (!$self->isAPIOwner()) { $newTOC = 0; } if (@_) { my $new = shift; # $new =~ s/\n//smgo; $new =~ s/\n/ /smgo; # Replace line returns by spaces $new =~ s/\s+$//smgo; # Remove trailing spaces if ($newTOC) { $self->{THROWS} .= "$new
\n"; } else { $self->{THROWS} .= "$new
\n"; } $self->{XMLTHROWS} .= "$new\n"; # print STDERR "Added $new to throw list.\n"; } # print STDERR "dumping throw list.\n"; if (length($self->{THROWS})) { if ($newTOC) { return $self->{THROWS}; } else { return ("

\n" . $self->{THROWS} . "

"); } } else { return ""; } } # /*! # @abstract # Returns the list of exceptions thrown by this # class/function. # @param self # The current object. # @discussion # The value is set with {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/throws//() throws}. # */ sub XMLthrows { my $self = shift; my $string = $self->htmlToXML($self->{XMLTHROWS}); my $ret; if (length($string)) { $ret = "\n$string\n"; } else { $ret = ""; } return $ret; } # /*! # @abstract # Gets/sets the abstract. # @param self # The current object. # @param throws # The new value. (Optional.) # @param isbrief # Use for compatibility the Doxygen \@brief tag. Pass 1 # for the \@brief behavior (abstract is limited to one paragraph # of content, so everything after a gap becomes part of the # discussion), 0 for the normal \@abstract behavior. # */ sub abstract { my $self = shift; my $localDebug = 0; my $isbrief = 0; if (@_) { my $abs = shift; print STDERR "Setting abstract for $self to \"$abs\"\n" if ($localDebug);; if (@_) { $isbrief = 1; } if ($isbrief) { my ($newabs, $newdisc) = split(/[\n\r][ \t]*[\n\r]/, $abs, 2); $abs = $newabs; $newdisc =~ s/^(\n|\r|< *br *\/? *>)*//si; $self->discussion($newdisc); } $self->{ABSTRACT} = $self->linkfix(filterHeaderDocTagContents($abs)); } my $ret = $self->{ABSTRACT}; # print STDERR "RET IS \"$ret\"\n"; # $ret =~ s/(\s*

<\/p>\s*)*$//sg; # This should no longer be needed. # $ret =~ s/(\s*

\s*)*$//sg; # print STDERR "RET IS \"$ret\"\n"; return $ret; } # /*! # @abstract # Returns the abstract for XML use. # @param self # This object. # @discussion # The value is set with # {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/abstract//() abstract}. # */ sub XMLabstract { my $self = shift; if (@_) { $self->{ABSTRACT} = shift; } return $self->htmlToXML($self->{ABSTRACT}); } # /*! # @abstract # Returns the nameline discussion by itself. # @param self # This object. # @discussion # Much like {@link nameline_discussion} except thiat # this variant does not scrape out HeaderDoc markup, # HTML tags, etc. # */ sub raw_nameline_discussion { my $self = shift; return $self->{NAMELINE_DISCUSSION}; } # /*! # @abstract # Returns the "raw" name. # @param self # This object. # @discussion # This function returns the name as taken from the # HeaderDoc comment. If the nameline contains multiple # words and there are additional nonempty discussion # lines, the entire name line is treated as the # name, and the nameline discussion is appended to # the value returned by {@link mediumrarename} to # obtain the value that this function returns. # # An explicit \@discussion tag forces the nameline discussion # to be treated as part of the name even if the contents of # the discussion tag are empty. # # This may be a separate value from the main name # returned by {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/name//() name} # and {@link mediumrarename}. # # If no "raw" name was set, returns the main name (equivalent to # {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/name//() name}. # */ sub rawname_extended { my $self = shift; my $localDebug = 0; my $n = $self->rawname(); # Append the rest of the name line if necessary. if ($self->{DISCUSSION_SET}) { # print STDERR "DISCUSSION IS: ".$self->{DISCUSSION}."\n"; # print STDERR "ISAPIO: ".$self->isAPIOwner()."\n"; # print STDERR "ISFW: ".$self->isFramework()."\n"; if ((!$HeaderDoc::ignore_apiowner_names) || (!$self->isAPIOwner()) || $self->isFramework()) { print STDERR "NAMELINE DISCUSSION for $self CONCATENATED (".$self->{NAMELINE_DISCUSSION}.")\n" if ($localDebug); print STDERR "ORIGINAL NAME WAS \"$n\"\n" if ($localDebug); if (length($self->{NAMELINE_DISCUSSION})) { $n .= " ".$self->{NAMELINE_DISCUSSION}; } } } return $n; } # /*! # @abstract # Gets/sets the nameline discussion. # @param self # This object. # @param disc # The new nameline discusison to set. (Optional.) # @discussion # With old-stype headerdoc comments (e.g. \@function), # the name line (the first line) contains the name and # an optional discussion. If the name line is not # the sole discussion, the remainder of the name line # is treated as part of the name. Thus, this line # gets stored separately. # # The content gets massively stripped by this function # under the assumption that it may get used as part # of a name. You should generally call this version # in that context and call {@link raw_nameline_discussion} # for other cases. # */ sub nameline_discussion { my $self = shift; my $localDebug = 0; if (@_) { $self->{NAMELINE_DISCUSSION} = shift; # cluck("in NL_DISC\n"); print STDERR "nameline discussion set to ".$self->{NAMELINE_DISCUSSION}."\n" if ($localDebug); } return $self->htmlToXML(filterHeaderDocTagContents($self->{NAMELINE_DISCUSSION})); } # /*! @abstract # Returns the discussion without merging in the name line. # @param self # This object. # */ sub raw_discussion { my $self = shift; return $self->{DISCUSSION}; } # /*! @abstract # Returns true if the discussion is set explicitly with an # \@discussion tag. # @param self # This object. # @discussion # An explicit \@discussion tag forces the nameline discussion # to be treated as part of the name even if the contents of # the discussion tag are empty. This flag supports that. # */ sub discussion_set { my $self = shift; return $self->{DISCUSSION_SET}; } # /*! # @abstract # Returns the discussion (without nameline discussion). # @param self # This object. # @discussion # See {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/name//() name} # for more info. # */ sub halfbaked_discussion { my $self = shift; return $self->discussion_sub(0, 0); } # /*! @abstract # Gets/sets the discussion value. # @param self # This object. # @param disc # The new value to set. (Optional.) # @discussion # This function returns the discussion, including the # nameline discussion, if needed. # */ sub discussion { my $self = shift; my $discDebug = 0; if (@_) { my $olddisc = $self->{DISCUSSION}; $self->{DISCUSSION_SET} = 1; print STDERR "DISCUSSION SET: $self : $olddisc -> \n" if ($discDebug); my $discussion = ""; if ($olddisc ne "" && $discussion ne "") { # Warn if we have multiple discussions. # We'll be quiet if we're in a define block, as # This is just the natural course of things. # Clear the old value out first, though. if (!$self->inDefineBlock()) { my $fullpath = $self->fullpath(); my $linenum = $self->linenum(); warn("$fullpath:$linenum: warning: Multiple discussions found for ".$self->name()." ($self). Merging.\n"); # print STDERR "OLDDISC: \"$olddisc\"\n"; # print STDERR "DISC: \"$discussion\"\n"; $discussion = $olddisc."

\n";; } } my $newdisc = $self->listfixup(shift); $discussion .= filterHeaderDocTagContents($newdisc); print STDERR "$discussion\n" if ($discDebug); # $discussion =~ s/
/\n/sgo; # $discussion =~ s/\n\n/
\n/go; $self->{DISCUSSION} = $self->linkfix($discussion); # warn("Set discussion to ".$self->{DISCUSSION}."\n"); # Ensure that the discussion is not completely blank. if ($self->{DISCUSSION} eq "") { $self->{DISCUSSION} .= " "; } # print "\$self->{DISCUSSION} = ".$self->{DISCUSSION}."\n" if ($discDebug); } return $self->discussion_sub(1, $discDebug); } # /*! @abstract # Gets the discussion value (private function). # @param self # This object. # @param bake # Determines whether to bake the results. # @param discDebug # Set to 1 for debugging, else 0. # @discussion # If a standard discussion is set, this function # returns that. If not, this returns the nameline # discussion. If code is set, the # nameline discussion is wrapped in paragraph tags. # */ sub discussion_sub { my $self = shift; my $bake = shift; my $discDebug = shift; # cluck("backtrace\n") if ($discDebug); print STDERR "OBJ is \"".$self."\"\n" if ($discDebug); print STDERR "NAME is \"".$self->{NAME}."\"\n" if ($discDebug); print STDERR "DISC WAS \"".$self->{DISCUSSION}."\"\n" if ($discDebug); print STDERR "NAMELINE DISC WAS \"".$self->{NAMELINE_DISCUSSION}."\"\n" if ($discDebug); # Return the real discussion if one exists. if ($self->{DISCUSSION}) { return $self->{DISCUSSION}; } else { print STDERR "RETURNING NAMELINE DISC\n" if ($discDebug); } # Return the contents of the name line (e.g. @struct foo Discussion goes here.) # beginning after the first token if no discussion exists. if ($bake) { return "

".$self->{NAMELINE_DISCUSSION}."

"; } return $self->{NAMELINE_DISCUSSION}; } # /*! # @abstract # Merges newlines and carriage returns with preceding text, but only if nonsequential. # @discussion # For proper interpreting of implicit lists (without ul or ol # tags), it is necessary to split the input into lines of text, keeping the first # newline/carriage return attached to the previous line, but not later newlines or # carriage returns. # @param arrayref # A reference to the input array. # @result # Returns the new array. # */ sub splitmerge { my $arrayref = shift; my @arr = @{$arrayref}; my @ret = (); foreach my $item (@arr) { if ($item =~ /[\n\r]/) { my $temp = pop(@ret); if ($temp) { if ($temp =~ /[\r\n]$/s) { push(@ret, $temp); } else { $item = $temp.$item; } } push(@ret, $item); } elsif ($item) { push(@ret, $item); } } return @ret; } # /*! # @abstract # Converts numbered lists in comments into HTML ordered # list tags where possible. # @param self # This object. # @param olddiscussion # The input discussion. # @return # Returns the new discussion. # */ sub listfixup { my $self = shift; my $olddiscussion = shift; my $discussion = ""; my $numListDebug = 0; my $encoding = $self->encoding(); # Note that HeaderDoc does not attempt to handle non-8-bit # character sets. my $bullet = ""; if ($encoding = /iso-8859-\d+\s*$/i) { # Technically, this is a lie. The ISO standards # don't specify any characters for this range. # That said, most software treats ISO-8859-1 like # Windows-1252 and calls 0x95 a bullet and the same # goes for the rest of the ISO character sets and # their Windows equivalents. $bullet = "\x95"; } elsif ($encoding = /Windows-\d+/i) { $bullet = "\x95"; # ISO 8859-1 } elsif ($encoding = /MacRoman/i) { $bullet = "\xA5"; } elsif ($encoding = /UTF-8/i) { $bullet = "\x{2022}"; } # I don't have time to actually write the bulleted list detection right now, # but this is where it will go eventually. Also, it would be nice to allow # nesting, though this will require tab to space conversion (e.g. with the -w # flag). That probably should happen anyway, and probably up a few levels # in the main text handling code). # print STDERR "ENC: $encoding\n"; if ($HeaderDoc::dumb_as_dirt) { print STDERR "BASIC MODE: LIST FIXUP DISABLED\n" if ($numListDebug); return $olddiscussion; } print STDERR "processing dicussion for ".$self->name().".\n" if ($numListDebug); my @disclines = split(/([\n\r])/, $olddiscussion); @disclines = splitmerge(\@disclines); my $curpos = 0; my $seekpos = 0; my $nlines = scalar(@disclines); my $oldinList = 0; my $intextblock = 0; my $inpre = 0; if ($numListDebug) { print STDERR "BEGIN DISCUSSION\n"; while ($curpos < $nlines) { my $line = $disclines[$curpos++]; print STDERR "$line"; } print STDERR "END DISCUSSION\n"; $curpos = 0; } while ($curpos < $nlines) { my $line = $disclines[$curpos]; if ($line =~ /\@textblock/) { $intextblock = 1; print STDERR "intextblock -> 1\n" if ($numListDebug); } if ($line =~ /\@\/textblock/) { $intextblock = 0; print STDERR "intextblock -> 0\n" if ($numListDebug); } if ($line =~ /
/) {
		$inpre = 1;
		print STDERR "inpre -> 1\n" if ($numListDebug);
	}
	if ($line =~ /<\/pre>/) {
		$inpre = 0;
		print STDERR "inpre -> 0\n" if ($numListDebug);
	}
	if ($intextblock || $inpre) {
		$discussion .= $line;
	} else {
		print STDERR "LINE: \"$line\"\n" if ($numListDebug);
			print STDERR "TOP OLDINLIST: $oldinList\n" if ($numListDebug);
		if ($line =~ /^\s*((?:-)?\d+)[\)\.\:\s]/o) {
			# this might be the first entry in a list.
			print STDERR "MAYBELIST: $line\n" if ($numListDebug);
			my $inList = 1;
			my $foundblank = 0;
			my $basenum = $1;
			$seekpos = $curpos + 1;
			my $added = 0;
			if (($seekpos >= $nlines) && !$oldinList) {
				$discussion .= "$line";
				$added = 1;
			} else {
			    while (($seekpos < $nlines) && ($inList == 1)) {
				my $scanline = $disclines[$seekpos];
				print STDERR "INLIST: $inList, OLDINLIST: $oldinList\n" if ($numListDebug);
				if ($scanline =~ /^<\/p>

\s*$/so || $scanline =~ /^\s*$/s) { # empty line $foundblank = 1; print STDERR "BLANKLINE\n" if ($numListDebug); } elsif ($scanline =~ /^\s*((?:-)?\d+)[\)\.\:\s]/o) { # line starting with a number $foundblank = 0; # print STDERR "D1 is $1\n"; if ($1 != ($basenum + 1)) { # They're noncontiguous. Not a list. print STDERR "NONCONTIG\n" if ($numListDebug); if (!$oldinList) { print STDERR "ADDED $line\n" if ($numListDebug); $discussion .= "$line"; $added = 1; } $inList = 0; } else { # They're contiguous. It's a list. print STDERR "CONTIG\n" if ($numListDebug); $inList = 2; } } else { # text. if ($foundblank && ($scanline =~ /\S+/o)) { # skipped a line and more text. # end the list here. print STDERR "LIST MAY END ON $scanline\n" if ($numListDebug); print STDERR "BASENUM IS $basenum\n" if ($numListDebug); $inList = 3; } } $seekpos++; } } if ($oldinList) { # we're finishing an existing list. $line =~ s/^\s*((?:-)?\d+)[\)\.\:\s]//so; $basenum = $1; $discussion .= "

  • $line"; print STDERR "LISTCONTINUES: $line\n" if ($numListDebug); } elsif ($inList == 3) { # this is a singleton. Don't touch it. $discussion .= $line; print STDERR "SINGLETON: $line\n" if ($numListDebug); } elsif ($inList == 2) { # this is the first entry in a list $line =~ s/^\s*((?:-)?\d+)[\)\.\:\s]//so; $basenum = $1; $discussion .= "
    1. $line"; print STDERR "FIRSTENTRY: $line\n" if ($numListDebug); } elsif (!$added) { $discussion .= $line; } if ($oldinList && !$inList) { $discussion .= "
    "; } $oldinList = $inList; } elsif ($line =~ /^<\/p>

    \s*$/so || $line =~ /^\s*$/s) { if ($oldinList == 3 || $oldinList == 1) { # If 3, this was last entry in list before next # text. If 1, this was last entry in list before # we ran out of lines. In either case, it's a # blank line not followed by another numbered # line, so we're done. print STDERR "OUTERBLANKLINE\n" if ($numListDebug); $discussion .= "

  • "; $oldinList = 0; } else { print STDERR "OIL: $oldinList\n" if ($numListDebug); $discussion .= "$line"; } } else { # $oldinList = 0; print STDERR "TEXTLINE: \"$line\"\n" if ($numListDebug); $discussion .= $line; } } $curpos++; } if ($oldinList) { $discussion .= ""; } print STDERR "done processing dicussion for ".$self->name().".\n" if ($numListDebug); # $newdiscussion = $discussion; return $discussion; } # /*! # @abstract # Returns the discussion formatted for XML. # @param self # This object. # @param deprecated_discussion # The value to set. (Optional, do not use.) # @discussion # Do not set discussion with this function. Use the # {@link //apple_ref/perl/instm/HeaderDoc::DocReference/discussion//() discussion} # function instead. # */ sub XMLdiscussion { my $self = shift; if (@_) { my $discussion = ""; $discussion = shift; # $discussion =~ s/\n\n/
    \n/go; $self->{DISCUSSION} = $discussion; } return $self->htmlToXML($self->{DISCUSSION}); } # /*! # @abstract # Gets/sets the vestigial text declaration. # @param self # This object. # @param declaration # The new declaration to set. (Optional.) # @discussion # This is only relevant if the -b flag is used, # and maybe not even then. # */ sub declaration { my $self = shift; # my $dec = $self->declarationInHTML(); # remove simple markup that we add to declarationInHTML # $dec =~s/
    /\n/gio; # $dec =~s//\n/gio; # $dec =~s/<\/font>/\n/gio; # $dec =~s/<(\/)?tt>//gio; # $dec =~s/<(\/)?b>//gio; # $dec =~s/<(\/)?pre>//gio; # $dec =~s/\ //gio; # $dec =~s/\<//gio; # $self->{DECLARATION} = $dec; # don't really have to have this ivar if (@_) { $self->{DECLARATION} = shift; } return $self->{DECLARATION}; } # /*! # @abstract # Gets/sets the prvate declaration for a C++ method. # @param self # This object. # @param newpridec # The new value to set. (Optional.) # @discussion # The private declaration for a C++ method are the portion # of the declaration after the semicolon. These define the # private internal variable names that correspond with the # public argument names. (Why!?!?!?!) # */ sub privateDeclaration { my $self = shift; if (@_) { $self->{PRIVATEDECLARATION} = shift; } return $self->{PRIVATEDECLARATION}; } # /*! # @abstract # Generate a cross-reference request in HTML style # @param keystring # string containing the keywords, e.g. struct or enum # @param namestring # string containing the type name itself # @param linktext # link text to generate # @param optional_expected_type # general genre of expected types # @discussion # This is used to simplify the code that rips apart the # resulting anchor when handling see or seealso tags. # */ sub genRefHTML($$$) { my $self = shift; my $keystring = shift; my $name = shift; my $linktext = shift; my $optional_expected_type = ""; my $of = $self->outputformat(); $self->outputformat("html"); my $result; if (@_) { $optional_expected_type = shift; $result = $self->genRef($keystring, $name, $linktext, $optional_expected_type); } else { $result = $self->genRef($keystring, $name, $linktext); } $self->outputformat($of); return $result; } # /*! # @abstract # Generate a cross-reference request (from a declaration) # @param keystring # string containing the keywords, e.g. struct or enum # @param namestring # string containing the type name itself # @param linktext # link text to generate # @param optional_expected_type # general genre of expected types # @param optional_return_only_ref # Pass 1 if you want the bare list of destinations. # If the symbol should not be linked, this mode # returns an empty string. # # Pass 0 (or omit) to get a marked up anchor with # link text enclosed. If the word should not be # linked, this mode returns the link text. # */ sub genRefFromDeclaration($$$$) { my $self = shift; my $keystring = shift; my $name = shift; my $linktext = shift; my $optional_expected_type = ""; if (@_) { $optional_expected_type = shift; } my $optional_return_only_ref = 0; if (@_) { $optional_return_only_ref = shift; } return $self->genRefCore($keystring, $name, $linktext, $optional_expected_type, $optional_return_only_ref, 1); } # /*! # @abstract # Generate a cross-reference request # @param keystring # string containing the keywords, e.g. struct or enum # @param namestring # string containing the type name itself # @param linktext # link text to generate # @param optional_expected_type # general genre of expected types # @param optional_return_only_ref # Pass 1 if you want the bare list of destinations. # If the symbol should not be linked, this mode # returns an empty string. # # Pass 0 (or omit) to get a marked up anchor with # link text enclosed. If the word should not be # linked, this mode returns the link text. # */ sub genRef($$$$) { my $self = shift; my $keystring = shift; my $name = shift; my $linktext = shift; my $optional_expected_type = ""; if (@_) { $optional_expected_type = shift; } my $optional_return_only_ref = 0; if (@_) { $optional_return_only_ref = shift; } return $self->genRefCore($keystring, $name, $linktext, $optional_expected_type, $optional_return_only_ref, 0); } # /*! # @abstract # Generate a cross-reference request # @param keystring # string containing the keywords, e.g. struct or enum # @param namestring # string containing the type name itself # @param linktext # link text to generate # @param expected_type # general genre of expected types # @param return_only_ref # Pass 1 if you want the bare list of destinations. # If the symbol should not be linked, this mode # returns an empty string. # # Pass 0 (or omit) to get a marked up anchor with # link text enclosed. If the word should not be # linked, this mode returns the link text. # */ sub genRefCore($$$$$$) { my $self = shift; my $keystring = shift; my $name = shift; my $linktext = shift; my $expected_type = shift; my $return_only_ref = shift; my $fromDeclaration = shift; my $fullpath = $self->fullpath(); my $linenum = $self->linenum(); my $tail = ""; my $xml = 0; my $localDebug = 0; print STDERR "NAME IS $name\n" if ($localDebug); if ($self->outputformat() eq "hdxml") { $xml = 1; } # Generate requests with sublang always (so that, for # example, a c++ header can link to a class from within # a typedef declaration. Generate anchors with lang # if the parent is a header, else sublang for stuff # within class braces so that you won't get name # resolution conflicts if something in a class has the # same name as a generic C entity, for example. my $lang = $self->sublang(); # my ($sotemplate, $eotemplate, $operator, $soc, $eoc, $ilc, $ilc_b, $sofunction, # $soprocedure, $sopreproc, $lbrace, $rbrace, $unionname, $structname, # $enumname, # $typedefname, $varname, $constname, $structisbrace, $macronamesref, # $classregexp, $classbraceregexp, $classclosebraceregexp, # $accessregexp, $requiredregexp, $propname, # $objcdynamicname, $objcsynthesizename, $moduleregexp, $definename, # $functionisbrace, $classisbrace, $lbraceconditionalre, $lbraceunconditionalre, $assignmentwithcolon, # $labelregexp, $parmswithcurlybraces, $superclasseswithcurlybraces, $soconstructor) = parseTokens($self->lang(), $self->sublang()); my %parseTokens = %{parseTokens($self->lang(), $self->sublang())}; my $accessregexp = $parseTokens{accessregexp}; if ($name =~ /^[\d\[\]]/o) { # Silently fail for [4] and similar. print STDERR "Silently fail[1]\n" if ($localDebug); if ($return_only_ref) { return ""; } return $linktext; } if (($name =~ /^[=|+-\/&^~!*]/o) || ($name =~ /^\s*\.\.\.\s*$/o)) { # Silently fail for operators # and varargs macros. print STDERR "Silently fail[2]\n" if ($localDebug); if ($return_only_ref) { return ""; } return $linktext; } # if (($name =~ /^\s*public:/o) || ($name =~ /^\s*private:/o) || # ($name =~ /^\s*protected:/o)) { if (length($accessregexp) && ($name =~ /$accessregexp(:)?/)) { # Silently fail for these, too. print STDERR "Silently fail[3]\n" if ($localDebug); if ($return_only_ref) { return ""; } return $linktext; } if ($name =~ s/\)\s*$//o) { if ($linktext =~ s/\)\s*$//o) { $tail = ")"; } else { warn("$fullpath:$linenum: warning: Parenthesis in ref name, not in link text\n"); warn("name: $name) linktext: $linktext\n"); } } # I haven't found any cases where this would trigger a warning # that don't already trigger a warning elsewhere. my $testing = 0; if ($testing && ($name =~ /&/o || $name =~ /\(/o || $name =~ /\)/o || $name =~ /.:(~:)./o || $name =~ /;/o || $name eq "::" || $name =~ /^::/o)) { my $classname = $self->name(); my $class = ref($self) || $self; my $declaration = $self->declaration(); if (($name eq "(") && $class eq "HeaderDoc::PDefine") { warn("$fullpath:$linenum: warning: bogus paren in #define\n"); } elsif (($name eq "(") && $class eq "HeaderDoc::Function") { warn("$fullpath:$linenum: warning: bogus paren in function\n"); } elsif ($class eq "HeaderDoc::Function") { warn("$fullpath:$linenum: warning: bogus paren in function\n"); } else { warn("$fullpath:$linenum: warning: $fullpath $classname $class $keystring generates bad crossreference ($name). Dumping trace.\n"); # my $declaration = $self->declaration(); # warn("BEGINDEC\n$declaration\nENDDEC\n"); $self->printObject(); } } if ($name =~ /^(.+)::(.+?)$/o) { my $classpart = $1; my $type = $2; if ($linktext !~ /::/o) { warn("$fullpath:$linenum: warning: Bogus link text generated for item containing class separator. Ignoring.\n"); } # print STDERR "CLASSPART: $classpart TYPE: $type\n"; my $ret = $self->genRef("class", $classpart, $classpart); $ret .= "::"; # This is where it gets ugly. C++ allows use of struct, # enum, and other similar types without preceding them # with struct, enum, etc.... # $classpart .= "/"; # Classpart is never empty here, so don't bother checking. my $ref1 = $self->genRefSub($lang, "instm", $type, $classpart); my $ref2 = $self->genRefSub($lang, "clm", $type, $classpart); my $ref3 = $self->genRefSub($lang, "func", $type, ""); my $ref4 = $self->genRefSub($lang, "ftmplt", $type, $classpart); my $ref5 = $self->genRefSub($lang, "defn", $type, ""); my $ref6 = $self->genRefSub($lang, "macro", $type, ""); # allow classes within classes for certain languages. my $ref7 = $self->genRefSub($lang, "cl", $type, $classpart); my $ref8 = $self->genRefSub($lang, "tdef", $type, ""); my $ref9 = $self->genRefSub($lang, "tag", $type, ""); my $ref10 = $self->genRefSub($lang, "econst", $type, ""); my $ref11 = $self->genRefSub($lang, "struct", $type, ""); my $ref12 = $self->genRefSub($lang, "data", $type, $classpart); my $ref13 = $self->genRefSub($lang, "clconst", $type, $classpart); my $ref14 = $self->genRefSub($lang, "intfm", $type, $classpart); my $ref99 = $self->genRefSub("doc/com", "intfm", $name, $classpart); my $lp = "$ref1 $ref2 $ref3 $ref4 $ref5 $ref6 $ref7 $ref8 $ref9 $ref10 $ref11 $ref12 $ref13 $ref14 $ref99"; if ($return_only_ref) { return $lp; } if (!$xml) { $ret .= "$type"; } else { $ret .= "$type"; } print STDERR "Double-colon case\n" if ($localDebug); return $ret.$tail; } my $ret = ""; my $apiUIDPrefix = HeaderDoc::APIOwner->apiUIDPrefix(); my $type = ""; my $className = ""; my $classNameImplicit = 0; my $apio = $self->apiOwner(); my $apioclass = ref($apio) || $apio; if ($apioclass ne "HeaderDoc::Header") { $classNameImplicit = 1; if ($apio->can("className")) { # to get the class name from Category objects $className = $apio->className(); } else { $className = $apio->name(); } # $className .= "/"; } my $class_or_enum_check = " $keystring "; # if ($lang eq "pascal") { $class_or_enum_check =~ s/\s+var\s+/ /sgo; } # if ($lang eq "MIG") { $class_or_enum_check =~ s/\s+(in|out|inout)\s+/ /sgo; } # $class_or_enum_check =~ s/\s+const\s+/ /sgo; # $class_or_enum_check =~ s/\s+static\s+/ /sgo; # $class_or_enum_check =~ s/\s+virtual\s+/ /sgo; # $class_or_enum_check =~ s/\s+auto\s+/ /sgo; # $class_or_enum_check =~ s/\s+extern\s+/ /sgo; # $class_or_enum_check =~ s/\s+__asm__\s+/ /sgo; # $class_or_enum_check =~ s/\s+__asm\s+/ /sgo; # $class_or_enum_check =~ s/\s+__inline__\s+/ /sgo; # $class_or_enum_check =~ s/\s+__inline\s+/ /sgo; # $class_or_enum_check =~ s/\s+inline\s+/ /sgo; # $class_or_enum_check =~ s/\s+register\s+/ /sgo; # $class_or_enum_check =~ s/\s+template\s+/ /sgo; # $class_or_enum_check =~ s/\s+unsigned\s+/ /sgo; # $class_or_enum_check =~ s/\s+signed\s+/ /sgo; # $class_or_enum_check =~ s/\s+volatile\s+/ /sgo; # $class_or_enum_check =~ s/\s+private\s+/ /sgo; # $class_or_enum_check =~ s/\s+protected\s+/ /sgo; # $class_or_enum_check =~ s/\s+public\s+/ /sgo; # $class_or_enum_check =~ s/\s+synchronized\s+/ /sgo; # $class_or_enum_check =~ s/\s+transient\s+/ /sgo; my ($case_sensitive, $keywordhashref) = $self->keywords(); my @keywords = keys %{$keywordhashref}; foreach my $keyword (@keywords) { if ($case_sensitive) { $class_or_enum_check =~ s/(^|\s+)\Q$keyword\E(\s+|$)/ /sg; } else { $class_or_enum_check =~ s/(^|\s+)\Q$keyword\E(\s+|$)/ /sgi; } } $class_or_enum_check =~ s/\s*//smgo; if (length($class_or_enum_check)) { SWITCH: { ($keystring =~ /type/o && $lang eq "pascal") && do { $type = "tdef"; last SWITCH; }; ($keystring =~ /type/o && $lang eq "MIG") && do { $type = "tdef"; last SWITCH; }; ($keystring =~ /record/o && $lang eq "pascal") && do { $type = "struct"; last SWITCH; }; ($keystring =~ /procedure/o && $lang eq "pascal") && do { $type = "*"; last SWITCH; }; ($keystring =~ /of/o && $lang eq "pascal") && do { $type = "*"; last SWITCH; }; ($keystring =~ /typedef/o) && do { $type = "tdef"; last SWITCH; }; (($keystring =~ /sub/o) && ($lang eq "perl")) && do { $type = "*"; last SWITCH; }; ($keystring =~ /function/o) && do { $type = "*"; last SWITCH; }; ($keystring =~ /typedef/o) && do { $type = "tdef"; last SWITCH; }; ($keystring =~ /struct/o) && do { $type = "tag"; last SWITCH; }; ($keystring =~ /union/o) && do { $type = "tag"; last SWITCH; }; ($keystring =~ /operator/o) && do { $type = "*"; last SWITCH; }; ($keystring =~ /enum/o) && do { $type = "tag"; last SWITCH; }; ($keystring =~ /protocol/o) && do { $type = "intf"; $classNameImplicit = 0; $className=$name; $name=""; last SWITCH; }; ($keystring =~ /class/o) && do { $type = "cl"; $classNameImplicit = 0; $className=$name; $name=""; last SWITCH; }; ($keystring =~ /#(define|ifdef|ifndef|if|endif|undef|elif|error|warning|pragma|include|import)/o) && do { # Used to include || $keystring =~ /class/o # defines and similar aren't followed by a type print STDERR "Keyword case\n" if ($localDebug); if ($return_only_ref) { return ""; } return $linktext.$tail; }; { $type = ""; my $name = $self->name(); warn "$fullpath:$linenum: warning: keystring ($keystring) in $name type link markup\n"; if ($return_only_ref) { return ""; } return $linktext.$tail; } } if ($type eq "*") { # warn "Function requested with genRef. This should not happen.\n"; # This happens now, at least for operators. my $lp = ""; if ($className ne "") { $lp .= " ".$self->genRefSub($lang, "instm", $name, $className); $lp .= " ".$self->genRefSub($lang, "clm", $name, $className); } $lp .= " ".$self->genRefSub($lang, "func", $name, ""); $lp .= " ".$self->genRefSub($lang, "ftmplt", $name, $className); $lp .= " ".$self->genRefSub($lang, "defn", $name, $className); $lp .= " ".$self->genRefSub($lang, "macro", $name, $className); $lp .= " ".$self->genRefSub("doc/com", "intfm", $name, $className); $lp =~ s/^ //s; print STDERR "Class or enum check case: Type is \"*\" case\n" if ($localDebug); if ($return_only_ref) { return $lp; } if (!$xml) { return "$linktext".$tail; } else { return "$linktext".$tail; } } else { print STDERR "Class or enum check case: Type is not \"*\" case\n" if ($localDebug); my $lp = $self->genRefSub($lang, $type, $className, $name); if ($return_only_ref) { return $lp; } if (!$xml) { return "$linktext".$tail; } else { return "$linktext".$tail; } } } else { # We could be looking for a class or a typedef. Unless it's local, put in both # and let the link resolution software sort it out. :-) my $typerefs = $self->genRefSub($lang, "cl", $name, ""); # allow classes within classes for certain languages. $typerefs .= " ".$self->genRefSub($lang, "cl", $name, $className) if (($className ne "") && !$classNameImplicit); $typerefs .= " ".$self->genRefSub($lang, "tdef", $name, ""); $typerefs .= " ".$self->genRefSub($lang, "tag", $name, ""); $typerefs .= " ".$self->genRefSub($lang, "struct", $name, ""); $typerefs .= " ".$self->genRefSub($lang, "intf", $name, ""); my $varrefs = $self->genRefSub($lang, "econst", $name, ""); $varrefs .= " ". $self->genRefSub($lang, "data", $name, $className); if ($classNameImplicit) { $varrefs .= " ".$self->genRefSub($lang, "data", $name, ""); } $varrefs .= " ".$self->genRefSub($lang, "clconst", $name, $className); my $functionrefs = $self->genRefSub($lang, "instm", $name, $className); $functionrefs .= " ".$self->genRefSub($lang, "clm", $name, $className); $functionrefs .= " ".$self->genRefSub($lang, "intfcm", $name, $className); $functionrefs .= " ".$self->genRefSub($lang, "intfm", $name, $className); $functionrefs .= " ".$self->genRefSub($lang, "func", $name, ""); $functionrefs .= " ".$self->genRefSub($lang, "ftmplt", $name, $className); $functionrefs .= " ".$self->genRefSub($lang, "defn", $name, ""); $functionrefs .= " ".$self->genRefSub($lang, "macro", $name, ""); my $docrefs = $self->genRefSub("doc/com", "intfm", $name, $className); my $anysymbol = $self->genRefSub("doc", "anysymbol", $name); my $masterref = "$typerefs $varrefs $functionrefs $docrefs $anysymbol"; print STDERR "Default case (OET: $expected_type)" if ($localDebug); if (length($expected_type)) { SWITCH: { ($expected_type eq "string") && do { if ($return_only_ref) { return ""; } return $linktext.$tail; }; ($expected_type eq "char") && do { if ($return_only_ref) { return ""; } return $linktext.$tail; }; ($expected_type eq "comment") && do { if ($return_only_ref) { return ""; } return $linktext.$tail; }; ($expected_type eq "preprocessor") && do { # We want to add links, but all we can # do is guess about the type. last SWITCH; }; ($expected_type eq "number") && do { if ($return_only_ref) { return ""; } return $linktext.$tail; }; ($expected_type eq "keyword") && do { if ($return_only_ref) { return ""; } return $linktext.$tail; }; ($expected_type eq "function") && do { $masterref = $functionrefs." ".$anysymbol; last SWITCH; }; ($expected_type eq "var") && do { # Variable name. if ($fromDeclaration) { $anysymbol = ""; } $masterref = $varrefs." ".$anysymbol; last SWITCH; }; ($expected_type eq "template") && do { # Could be any template parameter bit # (type or name). Since we don't care # much if a parameter name happens to # something (ideally, it shouldn't), # we'll just assume we're getting a # type and be done with it. $masterref = $typerefs." ".$anysymbol; last SWITCH; }; ($expected_type eq "type") && do { $masterref = $typerefs." ".$anysymbol; last SWITCH; }; ($expected_type eq "param") && do { # parameter name. Don't link. if ($return_only_ref) { return ""; } return $linktext.$tail; }; ($expected_type eq "ignore") && do { # hidden token. if ($return_only_ref) { return ""; } return $linktext.$tail; }; { warn("$fullpath:$linenum: warning: Unknown reference class \"$expected_type\" in genRef\n"); } } } print STDERR "Default case: No OET. MR IS $masterref\n" if ($localDebug); $masterref =~ s/\s+/ /g; if ($return_only_ref) { return $masterref; } if ($xml) { return "$linktext".$tail; } else { return "$linktext".$tail; } # return "$linktext".$tail; } } # /*! # @abstract # Returns all known keywords for the current language. # @param self # This object. # @result # The return value is an array ($case_sensitive, # \%keywords). # # The first value is either 0 or 1 depending on whether the # current language uses case-sensitive token matching or not. # # The second value is a reference to a keywords hash for the # current language. The specific value is usually 1. # Other values are used to indicate that the token should be # handled in a non-standard way in certain places. # # 1. most keywords. # 2. attributes and other things where the (...) afterwards # must be treated as part of the token. # 3. extends keyword. Used for indicating that what follows # is stored as the extends info for a class. # 4. implements keyword. Used for indicating that what follows # is stored as the implements info for a class. # 5. throws keywords. Used for indicating that what follows # is stored as as an exception that this function throws. # 6. __typeof__ keyword where the (...) afterwards must be # treated as part of the token; separate from #2 because # what follows is a type for code coloring purposes. # 7. extern keyword. Use for detecting extern "C". # 8. static and my keywords. Used for # determining whether global variables are file-scoped. # 9. case (shell). Changes parser parenthesis handling. # 10. esac (shell). Ends the case statement. # */ sub keywords { my $self = shift; my $class = ref($self) || $self; # my $declaration = shift; # my $functionBlock = shift; # my $orig_declaration = $declaration; my $localDebug = 0; my $parmDebug = 0; my $lang = $self->lang(); my $sublang = $self->sublang(); # my $fullpath = $HeaderDoc::headerObject->fullpath(); my $fullpath = $self->fullpath(); my $linenum = $self->linenum(); my $case_sensitive = 1; if (!$self->isAPIOwner()) { # $self->dbprint(); my $apio = $self->apiOwner(); # print STDERR "APIO: \"$apio\"\n"; return $apio->keywords(); } if ($self->{KEYWORDHASH}) { return ($self->{CASESENSITIVE}, $self->{KEYWORDHASH}); } print STDERR "keywords\n" if ($localDebug); # print STDERR "Color\n" if ($localDebug); # print STDERR "lang = $lang\n"; # Note: these are not all of the keywords of each language. # This should, however, be all of the keywords that can occur # in a function or data type declaration (e.g. the sort # of material you'd find in a header). If there are missing # keywords that meet that criterion, please file a bug. my %RubyKeywords = ( "assert" => 1, "break" => 1, "while" => 1, "if" => 1, "for" => 1, "until" => 1, "break" => 1, "redo" => 1, "retry" => 1, "nil" => 1, "true" => 1, "false" => 1, "class" => 1, "module" => 1); my %CKeywords = ( "assert" => 1, "break" => 1, "auto" => 1, "const" => 1, "enum" => 1, "extern" => 7, "inline" => 1, "__inline__" => 1, "__inline" => 1, "__asm" => 2, "__asm__" => 2, "__attribute__" => 2, "__typeof__" => 6, "register" => 1, "signed" => 1, "static" => 8, "struct" => 1, "typedef" => 1, "union" => 1, "unsigned" => 1, "volatile" => 1, "#define" => 1, "#ifdef" => 1, "#ifndef" => 1, "#if" => 1, "#endif" => 1, "#undef" => 1, "#elif" => 1, "#error" => 1, "#warning" => 1, "#pragma" => 1, "#include" => 1, "#import" => 1 , "NULL" => 1, "true" => 1, "false" => 1); my %CppKeywords = (%CKeywords, ("class" => 1, "friend" => 1, "mutable" => 1, "namespace" => 1, "operator" => 1, "private" => 1, "protected" => 1, "public" => 1, "template" => 1, "virtual" => 1)); my %ObjCKeywords = (%CKeywords, ("\@class" => 1, "\@interface" => 1, "\@protocol" => 1, "\@property" => 1, "\@public" => 1, "\@private" => 1, "\@protected" => 1, "\@package" => 1, "\@synthesize" => 1, "\@dynamic" => 1, "\@optional" => 1, "\@required" => 1, "nil" => 1, "YES" => 1, "NO" => 1 )); my %phpKeywords = (%CKeywords, ("function" => 1)); my %javascriptKeywords = ( "abstract" => 1, # "assert" => 1, "break" => 1, # "byte" => 1, "case" => 1, "catch" => 1, # "char" => 1, "class" => 1, "const" => 1, "continue" => 1, "debugger" => 1, "default" => 1, "delete" => 1, "do" => 1, # "double" => 1, "else" => 1, "enum" => 1, "export" => 1, "extends" => 3, "false" => 1, "final" => 1, "finally" => 1, # "float" => 1, "for" => 1, "function" => 1, "goto" => 1, "if" => 1, "implements" => 4, "import" => 1, "in" => 1, "instanceof" => 1, # "int" => 1, "interface" => 1, # "long" => 1, "native" => 1, "new" => 1, "null" => 1, "package" => 1, "private" => 1, "protected" => 1, "public" => 1, "return" => 1, # "short" => 1, "static" => 8, "super" => 1, "switch" => 1, "synchronized" => 1, "this" => 1, "throw" => 1, "throws" => 1, "transient" => 1, "true" => 1, "try" => 1, "typeof" => 1, "var" => 1, # "void" => 1, "volatile" => 1, "while" => 1, "with" => 1); my %javaKeywords = ( "abstract" => 1, "assert" => 1, "break" => 1, "case" => 1, "catch" => 1, "class" => 1, "const" => 1, "continue" => 1, "default" => 1, "do" => 1, "else" => 1, "enum" => 1, "extends" => 3, "false" => 1, "final" => 1, "finally" => 1, "for" => 1, "goto" => 1, "if" => 1, "implements" => 4, "import" => 1, "instanceof" => 1, "interface" => 1, "native" => 1, "new" => 1, "package" => 1, "private" => 1, "protected" => 1, "public" => 1, "return" => 1, "static" => 8, "strictfp" => 1, "super" => 1, "switch" => 1, "synchronized" => 1, "this" => 1, "throw" => 1, "throws" => 1, "transient" => 1, "true" => 1, "try" => 1, "volatile" => 1, "while" => 1); my %tclKeywords = ( "method" => 1, "constructor" => 1, "proc" => 1, "attribute" => 1 ); # Consider adding "regexp" command? # @@@ ADD "class" and fix bugs. my %perlKeywords = ( "sub" => 1, "my" => 8, "next" => 1, "last" => 1, "package" => 1 ); my %shellKeywords = ( "sub" => 1, "alias" => 1, "set" => 1, "alias" => 1, "if" => 1, "fi" => 1, "case" => 9, "esac" => 10 ); my %cshKeywords = ( "set" => 1, "setenv" => 1, "alias" => 1); my %pythonKeywords = ( "and" => 1, "assert" => 1, "break" => 1, "class" => 1, "continue" => 1, "def" => 1, "del" => 1, "elif" => 1, "else" => 1, "except" => 1, "exec" => 1, "finally" => 1, "for" => 1, "from" => 1, "global" => 1, "if" => 1, "import" => 1, "in" => 1, "is" => 1, "lambda" => 1, "not" => 1, "or" => 1, "pass" => 1, "print" => 1, "raise" => 1, "return" => 1, "try" => 1, "while" => 1, "yield" => 1 ); my %applescriptKeywords = ( "about" => 1, "above" => 1, "after" => 1, "against" => 1, "and" => 1, "apart from" => 1, "around" => 1, "as" => 1, "aside from" => 1, "at" => 1, "back" => 1, "before" => 1, "beginning" => 1, "behind" => 1, "below" => 1, "beneath" => 1, "beside" => 1, "between" => 1, "but" => 1, "by" => 1, "considering" => 1, "contain" => 1, "contains" => 1, "continue" => 1, "copy" => 1, "div" => 1, "does" => 1, "eighth" => 1, "else" => 1, "end" => 1, "equal" => 1, "equals" => 1, "error" => 1, "every" => 1, "exit" => 1, "false" => 1, "fifth" => 1, "first" => 1, "for" => 1, "fourth" => 1, "from" => 1, "front" => 1, "get" => 1, "given" => 1, "global" => 1, "if" => 1, "ignoring" => 1, "in" => 1, "instead of" => 1, "into" => 1, "is" => 1, "it" => 1, "its" => 1, "last" => 1, "local" => 1, "me" => 1, "middle" => 1, "mod" => 1, "my" => 1, "ninth" => 1, "not" => 1, "of" => 1, "on" => 1, "onto" => 1, "or" => 1, "out of" => 1, "over" => 1, "prop" => 1, "property" => 1, "put" => 1, "ref" => 1, "reference" => 1, "repeat" => 1, "return" => 1, "returning" => 1, "script" => 1, "second" => 1, "set" => 1, "seventh" => 1, "since" => 1, "sixth" => 1, "some" => 1, "tell" => 1, "tenth" => 1, "that" => 1, "the" => 1, "then" => 1, "third" => 1, "through" => 1, "thru" => 1, "timeout" => 1, "times" => 1, "to" => 1, "transaction" => 1, "true" => 1, "try" => 1, "until" => 1, "where" => 1, "while" => 1, "whose" => 1, "with" => 1, "without" => 1 ); my %pascalKeywords = ( "absolute" => 1, "abstract" => 1, "all" => 1, "and" => 1, "and_then" => 1, "array" => 1, "asm" => 1, "begin" => 1, "bindable" => 1, "case" => 1, "class" => 1, "const" => 1, "constructor" => 1, "destructor" => 1, "div" => 1, "do" => 1, "downto" => 1, "else" => 1, "end" => 1, "export" => 1, "file" => 1, "for" => 1, "function" => 1, "goto" => 1, "if" => 1, "import" => 1, "implementation" => 1, "inherited" => 1, "in" => 1, "inline" => 1, "interface" => 1, "is" => 1, "label" => 1, "mod" => 1, "module" => 1, "nil" => 1, "not" => 1, "object" => 1, "of" => 1, "only" => 1, "operator" => 1, "or" => 1, "or_else" => 1, "otherwise" => 1, "packed" => 1, "pow" => 1, "procedure" => 1, "program" => 1, "property" => 1, "qualified" => 1, "record" => 1, "repeat" => 1, "restricted" => 1, "set" => 1, "shl" => 1, "shr" => 1, "then" => 1, "to" => 1, "type" => 1, "unit" => 1, "until" => 1, "uses" => 1, "value" => 1, "var" => 1, "view" => 1, "virtual" => 1, "while" => 1, "with" => 1, "xor" => 1); my %IDLKeywords = ( "abstract" => 1, "any" => 1, "attribute" => 1, "case" => 1, # char "component" => 1, "const" => 1, "consumes" => 1, "context" => 1, "custom" => 1, "default" => 1, # double "exception" => 1, "emits" => 1, "enum" => 1, "eventtype" => 1, "factory" => 1, "FALSE" => 1, "finder" => 1, "fixed" => 1, # float "getraises" => 5, "getter" => 1, "home" => 1, "import" => 1, "in" => 1, "inout" => 1, "interface" => 1, "local" => 1, "long" => 1, "module" => 1, "multiple" => 1, "native" => 1, "Object" => 1, "octet" => 1, "oneway" => 1, "out" => 1, "primarykey" => 1, "private" => 1, "provides" => 1, "public" => 1, "publishes" => 1, "raises" => 5, "readonly" => 1, "setraises" => 5, "setter" => 1, "sequence" => 1, # short # string "struct" => 1, "supports" => 1, "switch" => 1, "TRUE" => 1, "truncatable" => 1, "typedef" => 1, "typeid" => 1, "typeprefix" => 1, "unsigned" => 1, "union" => 1, "uses" => 1, "ValueBase" => 1, "valuetype" => 1, # void # wchar # wstring "#define" => 1, "#ifdef" => 1, "#ifndef" => 1, "#if" => 1, "#endif" => 1, "#undef" => 1, "#elif" => 1, "#error" => 1, "#warning" => 1, "#pragma" => 1, "#include" => 1, "#import" => 1 ); my %MIGKeywords = ( "routine" => 1, "simpleroutine" => 1, "countinout" => 1, "inout" => 1, "in" => 1, "out" => 1, "subsystem" => 1, "skip" => 1, "#define" => 1, "#ifdef" => 1, "#ifndef" => 1, "#if" => 1, "#endif" => 1, "#undef" => 1, "#elif" =>1, "#error" => 1, "#warning" => 1, "#pragma" => 1, "#include" => 1, "#import" => 1, "import" => 1, "simport" => 1, "type" => 1, "skip" => 1, "serverprefix" => 1, "serverdemux" => 1, "userprefix" => 1 ); my $objC = 0; my %keywords = %CKeywords; # warn "Language is $lang, sublanguage is $sublang\n"; if ($lang eq "applescript") { %keywords = %applescriptKeywords; } if ($lang eq "python") { %keywords = %pythonKeywords; } if ($lang eq "ruby") { %keywords = %RubyKeywords; } if ($lang eq "C") { SWITCH: { ($sublang eq "cpp") && do { %keywords = %CppKeywords; last SWITCH; }; ($sublang eq "C") && do { last SWITCH; }; ($sublang =~ /^occ/o) && do { %keywords = %ObjCKeywords; $objC = 1; last SWITCH; }; #occ, occCat ($sublang eq "intf") && do { %keywords = %ObjCKeywords; $objC = 1; last SWITCH; }; ($sublang eq "MIG") && do { %keywords = %MIGKeywords; last SWITCH; }; ($sublang eq "IDL") && do { %keywords = %IDLKeywords; last SWITCH; }; warn "$fullpath:$linenum: warning: Unknown language ($lang:$sublang)\n"; } } if ($lang eq "Csource") { SWITCH: { ($sublang eq "Csource") && do { last SWITCH; }; ($sublang eq "cpp") && do { %keywords = %CppKeywords; last SWITCH; }; ($sublang eq "C") && do { last SWITCH; }; ($sublang =~ /^occ/o) && do { %keywords = %ObjCKeywords; $objC = 1; last SWITCH; }; #occ, occCat ($sublang eq "intf") && do { %keywords = %ObjCKeywords; $objC = 1; last SWITCH; }; warn "$fullpath:$linenum: warning: Unknown language ($lang:$sublang)\n"; } } if ($lang eq "php") { SWITCH: { ($sublang eq "php") && do { %keywords = %phpKeywords; last SWITCH; }; warn "$fullpath:$linenum: warning: Unknown language ($lang:$sublang)\n"; } } if ($lang eq "java") { SWITCH: { ($sublang eq "java") && do { %keywords = %javaKeywords; last SWITCH; }; ($sublang eq "javascript") && do { %keywords = %javascriptKeywords; last SWITCH; }; warn "$fullpath:$linenum: warning: Unknown language ($lang:$sublang)\n"; } } if ($lang eq "tcl") { SWITCH: { ($sublang eq "tcl") && do { %keywords = %tclKeywords; last SWITCH; }; warn "$fullpath:$linenum: warning: Unknown language ($lang:$sublang)\n"; } } if ($lang eq "perl") { SWITCH: { ($sublang eq "perl") && do { %keywords = %perlKeywords; last SWITCH; }; warn "$fullpath:$linenum: warning: Unknown language ($lang:$sublang)\n"; } } if ($lang eq "shell") { SWITCH: { ($sublang eq "csh") && do { %keywords = %cshKeywords; last SWITCH; }; ($sublang eq "shell") && do { %keywords = %shellKeywords; last SWITCH; }; warn "$fullpath:$linenum: warning: Unknown language ($lang:$sublang)\n"; } } if ($lang eq "pascal") { %keywords = %pascalKeywords; $case_sensitive = 0; } if ($lang eq "C" && $sublang eq "MIG") { $case_sensitive = 0; } # foreach my $keyword (sory %keywords) { # print STDERR "keyword $keyword\n"; # } $self->{KEYWORDHASH} = \%keywords; $self->{CASESENSITIVE} = $case_sensitive; # print STDERR "KEYS\n";foreach my $key (keys %keywords) { print STDERR "KEY: $key\n"; }print STDERR "ENDKEYS\n"; return ($case_sensitive, \%keywords); } # /*! # @abstract # Converts HTML to XHTML (fast version). # @param self # This object. # @param htmldata # The data to translate. # @param droppara # Unused. # @param debugname # A name to use to identify this block when printing debug info. # @discussion # This version calls into # {@link //apple_ref/perl/instm/HeaderDoc::Utilities/html2xhtml//() html2xhtml} # if the content contains tags. Otherwise, it # returns the original string immediately. # */ sub htmlToXML { my $self = shift; my $htmldata = shift; my $droppara = shift; my $debugname = shift; my $localDebug = 0; if ($htmldata !~ /[<>&]/o) { print STDERR "FASTPATH FOR $debugname\n" if ($localDebug); return $htmldata; } my $result = html2xhtml($htmldata, $self->encoding(), $debugname); print STDERR "FOR:\n$htmldata\nENDFOR\nRETURNING:\n$result\nENDRETURN\n" if ($localDebug); return $result; } # /*! # @abstract # Encodes text into HTML/XML entities. # @param self # This object. # @param textdata # The text to convert. # */ sub textToXML { my $self = shift; my $textdata = shift; $textdata =~ s/&/&/sgo; $textdata =~ s//>/sgo; return $textdata; } # /*! # @abstract # Prints the declaration with HTML or XML formatting. # @param self # This object. # @param declaration # The raw declaration. (Optional.) # @discussion # If you pass in something for declaration, # this function computes the declaration based on the # parse trees. If you do not pass an argument for # declaration, this function returns the # cached value from the last call (which could be empty). # */ sub declarationInHTML { my $self = shift; my $class = ref($self) || $self; my $localDebug = 0; # my $lang = $self->lang(); my $xml = 0; # my $priDec = wrap($self->privateDeclaration()); if ($self->outputformat() eq "hdxml") { $xml = 1; } if (@_) { # @@@ DISABLE STYLES FOR DEBUGGING HERE @@@ my $disable_styles = 0; if ($xml) { my $xmldec = shift; if (1 || $HeaderDoc::use_styles && !$disable_styles) { my $parseTree_ref = $self->parseTree(); my $parseTree = ${$parseTree_ref}; bless($parseTree, "HeaderDoc::ParseTree"); if ($self->can("isBlock") && $self->isBlock()) { $xmldec = ""; my @tree_refs = @{$self->parseTreeList()}; foreach my $tree_ref (@tree_refs) { my $tree = ${$tree_ref}; bless($tree, "HeaderDoc::ParseTree"); $xmldec .= $tree->xmlTree($self->preserve_spaces(), $self->hideContents())."\n"; } } else { $xmldec = $parseTree->xmlTree($self->preserve_spaces(), $self->hideContents()); } $self->{DECLARATIONINHTML} = $xmldec; } else { $self->{DECLARATIONINHTML} = $self->textToXML($xmldec); } return $xmldec; } my $declaration = shift; if (1 || $HeaderDoc::use_styles && !$disable_styles) { # print STDERR "I AM ".$self->name()." ($self)\n"; if ($self->can("isBlock") && $self->isBlock()) { # my $declaration = ""; my @defines = $self->parsedParameters(); ## foreach my $define (@defines) { ## # Force the declaration of each #define to get rebuilt. ## $declaration .= $define->declarationInHTML(); ## $declaration .= "\n"; ## } $declaration = ""; my @tree_refs = @{$self->parseTreeList()}; foreach my $tree_ref (@tree_refs) { my $tree = ${$tree_ref}; bless($tree, "HeaderDoc::ParseTree"); # print STDERR "Processing tree $tree\n"; # print STDERR $tree->htmlTree(); # print STDERR "\nEND TREE\n"; # $tree->dbprint(); $declaration .= $tree->htmlTree($self->preserve_spaces(), $self->hideContents())."\n"; } # print STDERR "END PROCESSING. DECLARATION IS:\n$declaration\nEND DECLARATION\n"; } else { my $parseTree_ref = $self->parseTree(); my $parseTree = ${$parseTree_ref}; bless($parseTree, "HeaderDoc::ParseTree"); # print STDERR "PT: ".$parseTree."\n"; # $parseTree->printTree(); $declaration = $parseTree->htmlTree($self->preserve_spaces(), $self->hideContents()); # print STDERR "HTMLTREE: $declaration\n"; } } # print STDERR "SET DECLARATION TO $declaration\n"; $self->{DECLARATIONINHTML} = $declaration; } return $self->{DECLARATIONINHTML}; } # /*! # @abstract # Gets/sets the parse tree associated with this object. # @param self # This object. # @param parsetree # A reference to the parse tree to set/add. (Optional.) # @discussion # If this is a block declaration, the parse tree is added to # its list of parse trees. # */ sub parseTree { my $self = shift; my $localDebug = 0; if (@_) { my $parsetree = shift; if ($self->can("isBlock") && $self->isBlock()) { $self->addParseTree($parsetree); } $self->{PARSETREE} = $parsetree; my $class = ref($self) || $self; if ($self->lang eq "applescript" && $class eq "HeaderDoc::Function") { if (!$self->{AS_CLASS_SELF}) { $self->cloneAppleScriptFunctionContents(); } if ($localDebug) { print STDERR "ADDING PARSE TREE FOR $self\n"; bless($parsetree, "HeaderDoc::ParseTree"); ${$parsetree}->dbprint(); } } } return $self->{PARSETREE}; } # /*! # @abstract # Returns the array of parse trees associated with this object. # @param self # This object. # */ sub parseTreeList { my $self = shift; my $localDebug = 0; if ($localDebug) { print STDERR "OBJ ".$self->name().":\n"; foreach my $treeref (@{$self->{PARSETREELIST}}) { my $tree = ${$treeref}; bless($tree, "HeaderDoc::ParseTree"); print STDERR "PARSE TREE: $tree\n"; $tree->dbprint(); } } if ($self->{PARSETREELIST}) { return $self->{PARSETREELIST}; } if (!$HeaderDoc::running_test) { die("No parse trees for object $self.\n". "Name is ".$self->name().". This usually\n". "points to a headerdoc comment before a #if whose contents are not a\n". "complete declaraction. This is a fatal error. Exiting.\n"); } # If we get here, we're being lazily called from the test framework, so return # an empty list of parse trees. my @arr = (); return \@arr; } # /*! # @abstract # Adds a parse tree to the array of parse trees associated with this object. # @param self # This object. # @param tree # A reference to the parse tree object to add. # */ sub addParseTree { my $self = shift; my $tree = shift; my $localDebug = 0; push(@{$self->{PARSETREELIST}}, $tree); if ($localDebug) { print STDERR "OBJ ".$self->name().":\n"; foreach my $treeref (@{$self->{PARSETREELIST}}) { my $tree = ${$treeref}; print STDERR "PARSE TREE: $tree\n"; bless($tree, "HeaderDoc::ParseTree"); $tree->dbprint(); } } } # /*! # @abstract # Adds the main parse tree to the parse tree list if missing. # @param self # This object. # */ sub fixParseTrees { my $self = shift; my $localDebug = 0; if (!$self->{PARSETREE}) { # Nothing to do. return; } my @trees = (); if ($self->{PARSETREELIST}) { @trees = @{$self->{PARSETREELIST}}; } my $match = 0; my $searchtree = ${$self->{PARSETREE}}; print STDERR "Looking for tree $searchtree\n" if ($localDebug); foreach my $treeref (@trees) { my $tree = ${$treeref}; print STDERR "Comparing with $tree\n" if ($localDebug); if ($tree == $searchtree) { $match = 1; } } if (!$match) { print STDERR "Not found. Adding\n" if ($localDebug); $self->addParseTree($self->{PARSETREE}); } else { print STDERR "Found. Not adding\n" if ($localDebug); } } # /*! # @abstract # Appends an availabiilty macro string to the original availability string # if it is missing. # @param self # This object. # @param orig # The original availability string. # */ sub availabilityAuto { my $self = shift; my $orig = shift; my $localDebug = 0; my $fullpath = $self->fullpath(); print STDERR "GENERATING AVAILABILITY FOR $self FP ".$self->fullpath()."\n" if ($localDebug); my $rangeref = $HeaderDoc::perHeaderRanges{$fullpath}; if ($localDebug) { print STDERR "FULLPATH: $fullpath\n"; foreach my $x (keys %HeaderDoc::perHeaderRanges) { print STDERR "PHR{$x} = ".$HeaderDoc::perHeaderRanges{$x}."\n"; } } my @ranges = @{$rangeref}; my $linenum = $self->linenum(); my $string = ""; print STDERR "IN AVAILABILITYAUTO (name is ".$self->name()."\n" if ($localDebug); foreach my $rangeref (@ranges) { print STDERR "RANGE $rangeref\n" if ($localDebug); my $range = ${$rangeref}; bless($range, "HeaderDoc::LineRange"); if ($range->inrange($linenum)) { my $newbit = $range->text(); my @pieces = split(/\;/, $newbit); foreach my $piece (@pieces) { my $nvpiece = $piece; $nvpiece =~ s/10\..*$//s; # print STDERR "SEARCH $string $newbit"; my $found = -1; if (($found = index(lc $orig, lc $nvpiece)) == -1) { if (($found = index(lc $string, lc $nvpiece)) == -1) { if (length($string)) { $string .= " "; } $string .= $piece."."; } } } } } print STDERR "LEAVING AVAILABILITYAUTO (RETURN IS $string)\n" if ($localDebug); return $string; } # /*! # @abstract # Gets/sets the availability for this object. # @param self # This object. # @param availstring # The availability string from the HeaderDoc comment. # */ sub availability { my $self = shift; if (@_) { $self->{AVAILABILITY} = shift; } my $string = $self->{AVAILABILITY}; my $add = $self->availabilityAuto($string); if (length($string) && length($add)) { $string .= " "; } return $string.$add; } # /*! # @abstract # Gets/sets the programming language for this object. # @param self # This object. # @param lang # The new value. (Optional.) # */ sub lang { my $self = shift; if (@_) { $self->{LANG} = shift; } return $self->{LANG}; } # /*! # @abstract # Gets/sets the programming language dialect for this object. # @param self # This object. # @param sublang # The new value. (Optional.) # @discussion # The dialect, sublang, represents a more specific language # than the main language in some cases. For example, with # C-based languages, the language is C, but the sublang can # be C (standard C, translated to a lowercase # c in the actual API reference), occ (Objective-C), # cpp (C++), etc. # */ sub sublang { my $self = shift; if (@_) { my $sublang = shift; if ($sublang eq "occCat") { $sublang = "occ"; } $self->{SUBLANG} = $sublang; } return $self->{SUBLANG}; } # /*! # @abstract # Gets/sets the last updated date for this object. # @param self # This object. # @param updated # The new value. (Optional.) # */ sub updated { my $self = shift; my $localdebug = 0; if (@_) { my $updated = shift; # $self->{UPDATED} = shift; my $month; my $day; my $year; $month = $day = $year = $updated; print STDERR "updated is $updated\n" if ($localdebug); if (!($updated =~ /\d\d\d\d-\d\d-\d\d/o )) { if (!($updated =~ /\d\d-\d\d-\d\d\d\d/o )) { if (!($updated =~ /\d\d-\d\d-\d\d/o )) { # my $fullpath = $HeaderDoc::headerObject->fullpath(); my $fullpath = $self->fullpath(); my $linenum = $self->linenum(); warn "$fullpath:$linenum: warning: Bogus date format: $updated.\n"; warn "$fullpath:$linenum: warning: Valid formats are MM-DD-YYYY, MM-DD-YY, and YYYY-MM-DD\n"; return $self->{UPDATED}; } else { $month =~ s/(\d\d)-\d\d-\d\d/$1/smog; $day =~ s/\d\d-(\d\d)-\d\d/$1/smog; $year =~ s/\d\d-\d\d-(\d\d)/$1/smog; my $century; $century = `date +%C`; $century *= 100; $year += $century; # $year += 2000; print STDERR "YEAR: $year" if ($localdebug); } } else { print STDERR "03-25-2003 case.\n" if ($localdebug); $month =~ s/(\d\d)-\d\d-\d\d\d\d/$1/smog; $day =~ s/\d\d-(\d\d)-\d\d\d\d/$1/smog; $year =~ s/\d\d-\d\d-(\d\d\d\d)/$1/smog; } } else { $year =~ s/(\d\d\d\d)-\d\d-\d\d/$1/smog; $month =~ s/\d\d\d\d-(\d\d)-\d\d/$1/smog; $day =~ s/\d\d\d\d-\d\d-(\d\d)/$1/smog; } $month =~ s/\n//smog; $day =~ s/\n//smog; $year =~ s/\n//smog; $month =~ s/\s*//smog; $day =~ s/\s*//smog; $year =~ s/\s*//smog; # Check the validity of the modification date my $invalid = 0; my $mdays = 28; if ($month == 2) { if ($year % 4) { $mdays = 28; } elsif ($year % 100) { $mdays = 29; } elsif ($year % 400) { $mdays = 28; } else { $mdays = 29; } } else { my $bitcheck = (($month & 1) ^ (($month & 8) >> 3)); if ($bitcheck) { $mdays = 31; } else { $mdays = 30; } } if ($month > 12 || $month < 1) { $invalid = 1; } if ($day > $mdays || $day < 1) { $invalid = 1; } if ($year < 1970) { $invalid = 1; } if ($invalid) { # my $fullpath = $HeaderDoc::headerObject->fullpath(); my $fullpath = $self->fullpath(); my $linenum = $self->linenum(); warn "$fullpath:$linenum: warning: Invalid date (year = $year, month = $month, day = $day).\n"; warn "$fullpath:$linenum: warning: Valid formats are MM-DD-YYYY, MM-DD-YY, and YYYY-MM-DD\n"; return $self->{UPDATED}; } else { $self->{UPDATED} = HeaderDoc::HeaderElement::strdate($month-1, $day, $year, $self->encoding()); print STDERR "date set to ".$self->{UPDATED}."\n" if ($localdebug); } } return $self->{UPDATED}; } # /*! # @abstract # Gets/sets the linkage state for this object. # @param self # This object. # @param linkagestate # The new value. (Optional.) # */ sub linkageState { my $self = shift; if (@_) { $self->{LINKAGESTATE} = shift; } return $self->{LINKAGESTATE}; } # /*! # @abstract # Gets/sets the access control (public/private) for this object. # @param self # This object. # @param access # The new value. (Optional.) # */ sub accessControl { my $self = shift; if (@_) { $self->{ACCESSCONTROL} = shift; } return $self->{ACCESSCONTROL}; } # /*! # @abstract # Prints this object for debugging purposes. # @param self # This object. # */ sub printObject { my $self = shift; my $dec = $self->declaration(); print STDERR "------------------------------------\n"; print STDERR "HeaderElement\n"; print STDERR "name: $self->{NAME}\n"; print STDERR "abstract: $self->{ABSTRACT}\n"; print STDERR "declaration: $dec\n"; print STDERR "declaration in HTML: $self->{DECLARATIONINHTML}\n"; print STDERR "discussion: $self->{DISCUSSION}\n"; print STDERR "linkageState: $self->{LINKAGESTATE}\n"; print STDERR "accessControl: $self->{ACCESSCONTROL}\n\n"; print STDERR "Tagged Parameters:\n"; my $taggedParamArrayRef = $self->{TAGGEDPARAMETERS}; if ($taggedParamArrayRef) { my $arrayLength = @{$taggedParamArrayRef}; if ($arrayLength > 0) { &printArray(@{$taggedParamArrayRef}); } print STDERR "\n"; } my $fieldArrayRef = $self->{CONSTANTS}; if ($fieldArrayRef) { my $arrayLength = @{$fieldArrayRef}; if ($arrayLength > 0) { &printArray(@{$fieldArrayRef}); } print STDERR "\n"; } } # /*! # @abstract # Retargets HTML links. # @param self # This object. # @param inpString # The string of HTML to fix. # */ sub linkfix { my $self = shift; my $inpString = shift; my @parts = split(/\)/so; my $linkpart = $1; my $rest = $part; $rest =~ s/^\Q$linkpart\E//s; print STDERR "Found link.\nlinkpart: $linkpart\nrest: $rest\n" if ($localDebug); if ($linkpart =~ /target\=\".*\"/sio) { print STDERR "link ok\n" if ($localDebug); $outString .= "<$part"; } else { print STDERR "needs fix.\n" if ($localDebug); $linkpart =~ s/\>$//so; $outString .= "<$linkpart target=\"_top\">$rest"; } } else { $outString .= "<$part"; } } } return $outString; } # /*! @abstract # A function for converting numeric dates into strings. # @param month # Month in the range 0-11 (not 1-12). # @param day # Day of the month. # @param year # Year (four-digit). # @var format # A time format suitable for strftime. # Extracted from the HeaderDoc configuration file. # Also contains a compatibility hack for compatibility with # the old HeaderDoc date format codes. # */ sub strdate($$$$) { my $month = shift; my $day = shift; my $year = shift; my $encoding = shift; my $format = $HeaderDoc::datefmt; if (!defined $format) { $format = "%B %d, %Y"; } my $time_t = mktime(0, 0, 0, $day, $month, $year-1900); my ($sec,$min,$hour,$mday,$mon,$yr,$wday,$yday,$isdst) = localtime($time_t); my $time = strftime($format, $sec, $min, $hour, $mday, $mon, $yr, $wday, $yday, $isdst); my $current_encoding = getDefaultEncoding(); # die("CURENC: ".$current_encoding); # cluck("TIME: $time ENC: $encoding"); my $perltimestring = decode($current_encoding, $time); if ($encoding eq "macintosh") { return encode("mac_roman", $perltimestring); } else { return encode($encoding, $perltimestring); } return $time; # print STDERR "format $format\n"; if ($format eq "") { return "$month/$day/$year"; } else { my $dateString = ""; my $firstsep = ""; if ($format =~ /^.(.)/o) { $firstsep = $1; } my $secondsep = ""; if ($format =~ /^...(.)./o) { $secondsep = $1; } SWITCH: { ($format =~ /^M/io) && do { $dateString .= "$month$firstsep" ; last SWITCH; }; ($format =~ /^D/io) && do { $dateString .= "$day$firstsep" ; last SWITCH; }; ($format =~ /^Y/io) && do { $dateString .= "$year$firstsep" ; last SWITCH; }; print STDERR "Unknown date format ($format) in config file[1]\n"; print STDERR "Assuming MDY\n"; return "$month/$day/$year"; } SWITCH: { ($format =~ /^..M/io) && do { $dateString .= "$month$secondsep" ; last SWITCH; }; ($format =~ /^..D/io) && do { $dateString .= "$day$secondsep" ; last SWITCH; }; ($format =~ /^..Y/io) && do { $dateString .= "$year$secondsep" ; last SWITCH; }; ($firstsep eq "") && do { last SWITCH; }; print STDERR "Unknown date format ($format) in config file[2]\n"; print STDERR "Assuming MDY\n"; return "$month/$day/$year"; } SWITCH: { ($format =~ /^....M/io) && do { $dateString .= "$month" ; last SWITCH; }; ($format =~ /^....D/io) && do { $dateString .= "$day" ; last SWITCH; }; ($format =~ /^....Y/io) && do { $dateString .= "$year" ; last SWITCH; }; ($secondsep eq "") && do { last SWITCH; }; print STDERR "Unknown date format ($format) in config file[3]\n"; print STDERR "Assuming MDY\n"; return "$month/$day/$year"; } return $dateString; } } # /*! # @abstract # Sets the CSS for a particular style name. # @param self # This object. # @param name # The name of the style. # @param style # The CSS style data. # */ sub setStyle { my $self = shift; my $name = shift; my $style = shift; $style =~ s/^\s*//sgo; $style =~ s/\s*$//sgo; if (length($style)) { $CSS_STYLES{$name} = $style; $HeaderDoc::use_styles = 1; } } # Note: the backslashes before the @ signs in the comment below are # to prevent HeaderDoc from interpreting the tags. # /*! # @abstract # HTML/XML fixup code to insert superclass discussions # @param self # This object. # @param html # The block of (discussion) HTML to process. # @discussion # This code inserts the discussion from the superclass wherever # <hd_ihd/> appears if possible (i.e. where \@inheritDoc (HeaderDoc) # or {\@inheritDoc} (JavaDoc) appears in the original input material. # # For XML, <hd_ihd> and </hd_ihd> tags surround the # inherited content. # */ sub fixup_inheritDoc { my $self = shift; my $html = shift; my $newhtml = ""; my @pieces = split(///so) { if ($self->outputformat() eq "hdxml") { $newhtml .= ""; } $newhtml .= $self->inheritDoc(); if ($self->outputformat() eq "hdxml") { $newhtml .= ""; } $newhtml .= "$piece"; } else { $newhtml .= "<$piece"; } } $newhtml =~ s/^ appears (i.e. where # \@value (HeaderDoc) or {\@value} (JavaDoc) appears in the original # input material. # */ sub fixup_values { my $self = shift; my $html = shift; my $newhtml = ""; my @pieces = split(///so) { if ($self->outputformat() eq "hdxml") { $newhtml .= ""; } $newhtml .= $self->value(); if ($self->outputformat() eq "hdxml") { $newhtml .= ""; } $newhtml .= "$piece"; } else { $newhtml .= "<$piece"; } } $newhtml =~ s/^setStyle("function", "background:#ffff80; color:#000080;"); # $self->setStyle("text", "background:#000000; color:#ffffff;"); # print STDERR "results:\n"; # print STDERR "function: \"".$self->getStyle("function")."\"\n"; # print STDERR "text: \"".$self->getStyle("text")."\"\n"; # } if ($TOC) { if (defined($HeaderDoc::externalTOCStyleSheets)) { $css .= $self->doExternalStyle($HeaderDoc::externalTOCStyleSheets); $stdstyles = 0; } elsif ($HeaderDoc::externalStyleSheets) { $css .= $self->doExternalStyle($HeaderDoc::externalStyleSheets); $stdstyles = 0; } } elsif ($HeaderDoc::externalStyleSheets) { $css .= $self->doExternalStyle($HeaderDoc::externalStyleSheets); $stdstyles = 0; } if ($HeaderDoc::suppressDefaultStyles) { $stdstyles = 0; } $css .= ""; return $css; } # /*! # @abstract # Returns the documentation output for this object (in HTML). # @param self # This object. # @param composite # Pass 1 if generating a composite page and classAsComposite # is 0. Otherwise, pass 0. # */ sub documentationBlock { my $self = shift; my $composite = shift; my $contentString; my $name = $self->name(); my $desc = $self->discussion(); my $checkDisc = $self->halfbaked_discussion(); my $throws = ""; my $abstract = $self->abstract(); my $availability = $self->availability(); my $namespace = ""; if ($self->can("namespace")) { $namespace = $self->namespace(); } my $updated = $self->updated(); my $declaration = ""; my $result = ""; my $localDebug = 0; # my $apiUIDPrefix = HeaderDoc::APIOwner->apiUIDPrefix(); my $fullpath = $self->fullpath(); my $linenum = $self->linenum(); my $list_attributes = $self->getAttributeLists($composite); my $short_attributes = $self->getAttributes(0); my $long_attributes = $self->getAttributes(1); my $class = ref($self) || $self; my $apio = $self->apiOwner(); my $apioclass = ref($apio) || $apio; my $apiref = ""; my $headlevel = "h3"; my $newTOCinAPIOwner = $HeaderDoc::newTOC; my $showDiscussionHeading = 1; my $isAPIO = $self->isAPIOwner(); if ($self->{HIDEDOC}) { return ""; } # print STDERR "$self (".$self->name().") ISINTERNAL: ".$self->isInternal()." DOIT: ".$HeaderDoc::document_internal."\n"; if ($self->isInternal() && !$HeaderDoc::document_internal) { return ""; } my @embeddedClasses = (); my $class_self = undef; if ($self->lang eq "applescript" && $class eq "HeaderDoc::Function") { if (!$self->{ASCONTENTSPROCESSED}) { $class_self = $self->processAppleScriptFunctionContents(); if ($class_self) { my @classes = $class_self->classes(); foreach my $obj (@classes) { push(@embeddedClasses, $obj); } } } } # print STDERR "GOTHERE\n"; # Only use this style for API Owners. if ($isAPIO) { $headlevel = "h1"; if ($newTOCinAPIOwner) { $showDiscussionHeading = 0; } if (($checkDisc !~ /\S/) && ($abstract !~ /\S/)) { my $linenum = $self->linenum(); warn "$fullpath:$linenum: No header or class discussion/abstract found. Creating dummy file for default content page.\n"; $abstract .= $HeaderDoc::defaultHeaderComment; # "Use the links in the table of contents to the left to access documentation.
    \n"; } } else { $newTOCinAPIOwner = 0; $declaration = $self->declarationInHTML(); } # print STDERR "NAME: $name APIOCLASS: $apioclass APIUID: ".$self->apiuid()."\n"; if ($self->can("result")) { $result = $self->result(); } if ($self->can("throws")) { $throws = $self->throws(); } if ($self->noRegisterUID()) { cluck("BT\n"); die("Unexpected unregistered object being inserted into content. Object is $self, name is ".$self->name().", header is ".$apio->name()."\n"); } # $name =~ s/\s*//smgo; if ($isAPIO) { if ($self->htmlHeader() =~ /\S/) { $contentString .= "
    "; } } else { $contentString .= "
    "; } # my $uid = "//$apiUIDPrefix/c/func/$name"; # registerUID($uid); # $contentString .= "\n"; # apple_ref marker my ($constantsref, $fieldsref, $paramsref, $fieldHeading, $func_or_method, $variablesref)=$self->apirefSetup(); my @local_variables = @{$variablesref}; my @constants = @{$constantsref}; my @fields = @{$fieldsref}; my @params = @{$paramsref}; $apiref = $self->apiref($composite); my $divid = "docbox_".$self->apiuid(); # if ($HeaderDoc::enable_custom_references) { # $contentString .= "
    \n"; # } if (!$isAPIO) { $contentString .= $apiref; } if ($HeaderDoc::newTOC != 5) { $contentString .= ""; $contentString .= ""; $contentString .= ""; $contentString .= "
    "; } my $urlname = sanitize($name, 1); $contentString .= "<$headlevel>$name\n"; # if ($HeaderDoc::enable_custom_references && $HeaderDoc::newTOC && !$isAPIO) { # # Apple-style TOC. Assume the JavaScript is there. # $contentString .= "

    Add to custom reference

    \n"; # $contentString .= "

    Remove from custom reference

    \n"; # } if ($HeaderDoc::newTOC != 5) { $contentString .= "
    "; } if (!$newTOCinAPIOwner) { $contentString .= "
    "; } my $attstring = ""; my $c = 0; if (length($short_attributes)) { $attstring .= $short_attributes; $c++; } if (length($list_attributes)) { $attstring .= $list_attributes; $c++; } # print STDERR "ATS: $attstring\n"; if ($newTOCinAPIOwner) { if ($c == 2) { $attstring =~ s/<\/table><\/div>\s*//s; } $attstring =~ s/<\/table><\/div>\s*$//s; } if (!$newTOCinAPIOwner) { $attstring .= "
    "; } if (length($throws)) { if ($newTOCinAPIOwner) { if (!$c) { $attstring .= "
    \n"; } $attstring .= "\n"; } else { $attstring .= "
    Throws
    \n
    $throws
    \n"; } $c++; } my $includeList = ""; if ($class eq "HeaderDoc::Header") { my $includeref = $HeaderDoc::perHeaderIncludes{$fullpath}; if ($includeref) { my @includes = @{$includeref}; my $first = 1; foreach my $include (@includes) { my $localDebug = 0; print STDERR "Included file: $include\n" if ($localDebug); if (!$first) { if ($newTOCinAPIOwner) {$includeList .= "
    \n"; } else {$includeList .= ",\n"; } } my $xmlinc = $self->textToXML($include); my $includeguts = $include; $includeguts =~ s/[<\"](.*)[>\"]/$1/so; my $includefile = basename($includeguts); my $ref = $self->genRefSub("doc", "header", $includefile, ""); $includeList .= "$xmlinc"; $first = 0; } } } if (length($includeList)) { if ($newTOCinAPIOwner) { if (!$c) { $attstring .= "
    Throws:
    $throws
    \n"; } $attstring .= "\n"; } else { $attstring .= "Includes "; $attstring .= $includeList; $attstring .= "
    \n"; } $c++; } # if (length($abstract)) { # $contentString .= "
    Abstract:
    \n
    $abstract
    \n"; # } if ($newTOCinAPIOwner) { if ($c) { $attstring .= "
    Includes:
    $includeList
    \n"; } # Otherwise we do this later. $contentString .= $attstring; } else { if ($attstring =~ /
    \s*$/s) { $attstring =~ s/
    \s*$//s; } else { $attstring .= "
    "; } } if ($newTOCinAPIOwner) { $contentString .= "

    ".$HeaderDoc::introductionName."

    \n"; } my $uid = $self->apiuid(); if (length($abstract)) { $showDiscussionHeading = 1; # $contentString .= "
    Abstract:
    \n
    $abstract
    \n"; my $absstart = $self->headerDocMark("abstract", "start"); my $absend = $self->headerDocMark("abstract", "end"); $contentString .= "

    $absstart"; if ($self->can("isFramework") && $self->isFramework()) { $contentString .= "\n"; } $contentString .= "$abstract"; if ($self->can("isFramework") && $self->isFramework()) { $contentString .= "\n"; } $contentString .= "$absend

    \n"; } my $accessControl = ""; if ($self->can("accessControl")) { $accessControl = $self->accessControl(); } my $optionalOrRequired = ""; if ($self->parserState && ($apioclass =~ /HeaderDoc::ObjCProtocol/)) { $optionalOrRequired = $self->parserState->{optionalOrRequired}; } my $includeAccess = 0; if ($accessControl ne "") { $includeAccess = 1; } if ($self->can("isProperty") && $self->isProperty()) { $includeAccess = 0; } if ($self->class eq "HeaderDoc::Method") { $includeAccess = 0; } if ($self->class eq "HeaderDoc::PDefine") { $includeAccess = 0; } if ($self->lang eq "perl") { $includeAccess = 0; } if (!$isAPIO) { $contentString .= "
    \n"; my $declstart = $self->headerDocMark("declaration", "start"); my $declend = $self->headerDocMark("declaration", "end"); if ($includeAccess) { $contentString .= "
    $accessControl\n
    $declaration
    \n"; } elsif (length $optionalOrRequired) { $contentString .= "
    $optionalOrRequired\n
    $declaration
    \n"; } else { $contentString .= "
    $declstart$declaration$declend
    \n"; } $contentString .= "
    \n"; } my @parameters_or_fields = (); my @callbacks = (); foreach my $element (@params) { if ($element->isCallback()) { push(@callbacks, $element); } elsif (!$element->{ISDEFINE}) { push(@parameters_or_fields, $element); } } foreach my $element (@fields) { if ($element->isCallback()) { push(@callbacks, $element); } elsif (!$element->{ISDEFINE}) { push(@parameters_or_fields, $element); } } my @includedDefines = (); if ($self->{INCLUDED_DEFINES}) { @includedDefines = @{$self->{INCLUDED_DEFINES}}; } my $arrayLength = @includedDefines; if (($arrayLength > 0)) { my $paramContentString; $showDiscussionHeading = 1; foreach my $element (@includedDefines) { # print "ELT IS $element\n"; if ($self->{HIDESINGLETONS}) { if ($element->{MAINOBJECT}) { $element = ${$element->{MAINOBJECT}}; # print "ELEMENT NOW $element\n"; bless($element, "HeaderDoc::HeaderElement"); # print "ELEMENT NOW $element\n"; bless($element, $element->class()); # print "ELEMENT NOW $element\n"; # print "ELEMENT NAME ".$element->{NAME}."\n"; } } my $fName = $element->name(); my $fDesc = $element->discussion(); my $fType = ""; my $apiref = ""; if ($self->can("type")) { $fType = $element->type(); } $apiref = $element->apiref($composite); # , $apiRefType); if (length ($fName) && (($fType eq 'field') || ($fType eq 'constant') || ($fType eq 'funcPtr') || ($fType eq ''))) { # $paramContentString .= "$fName$fDesc\n"; $paramContentString .= "
    $apiref$fName
    $fDesc
    \n"; } elsif ($fType eq 'callback') { my @userDictArray = $element->userDictArray(); # contains elements that are hashes of param name to param doc my $paramString; foreach my $hashRef (@userDictArray) { while (my ($param, $disc) = each %{$hashRef}) { $paramString .= "
    $param
    \n
    $disc
    \n"; } if (length($paramString)) { $paramString = "
    \n".$paramString."\n
    \n"; }; } # $contentString .= "$fName$fDesc
    $paramString\n"; $contentString .= "
    $fName
    $fDesc
    $paramString
    \n"; } else { # my $fullpath = $HeaderDoc::headerObject->name(); my $classname = ref($self) || $self; $classname =~ s/^HeaderDoc:://o; if (!$HeaderDoc::ignore_apiuid_errors) { print STDERR "$fullpath:$linenum: warning: $classname ($name) field with name $fName has unknown type: $fType\n"; } } } if (length ($paramContentString)){ $contentString .= "
    Included Defines
    \n"; $contentString .= "
    \n"; # $contentString .= "\n"; # $contentString .= "\n"; $contentString .= "
    \n"; $contentString .= $paramContentString; # $contentString .= "
    NameDescription
    \n
    \n"; $contentString .= "
    \n"; $contentString .= "
    \n"; } } $arrayLength = @parameters_or_fields; if (($arrayLength > 0) && (length($fieldHeading))) { my $paramContentString; $showDiscussionHeading = 1; foreach my $element (@parameters_or_fields) { my $fName = $element->name(); my $fDesc = $element->discussion(); my $fType = ""; my $apiref = ""; if ($self->can("type")) { $fType = $element->type(); } $apiref = $element->apiref($composite); # , $apiRefType); if (length ($fName) && (($fType eq 'field') || ($fType eq 'constant') || ($fType eq 'funcPtr') || ($fType eq ''))) { # $paramContentString .= "$fName$fDesc\n"; $paramContentString .= "
    $apiref$fName
    $fDesc
    \n"; } elsif ($fType eq 'callback') { my @userDictArray = $element->userDictArray(); # contains elements that are hashes of param name to param doc my $paramString; foreach my $hashRef (@userDictArray) { while (my ($param, $disc) = each %{$hashRef}) { $paramString .= "
    $param
    \n
    $disc
    \n"; } if (length($paramString)) { $paramString = "
    \n".$paramString."\n
    \n"; }; } # $contentString .= "$fName$fDesc
    $paramString\n"; $contentString .= "
    $fName
    $fDesc
    $paramString
    \n"; } else { # my $fullpath = $HeaderDoc::headerObject->name(); my $classname = ref($self) || $self; $classname =~ s/^HeaderDoc:://o; if (!$HeaderDoc::ignore_apiuid_errors) { print STDERR "$fullpath:$linenum: warning: $classname ($name) field with name $fName has unknown type: $fType\n"; } } } if (length ($paramContentString)){ $contentString .= "
    $fieldHeading
    \n"; $contentString .= "
    \n"; # $contentString .= "\n"; # $contentString .= "\n"; $contentString .= "
    \n"; $contentString .= $paramContentString; # $contentString .= "
    NameDescription
    \n
    \n"; $contentString .= "\n"; $contentString .= "\n"; } } if (@embeddedClasses) { $showDiscussionHeading = 1; $contentString .= "
    Embedded Classes
    \n"; $contentString .= "
    \n"; $contentString .= "
    \n"; # $contentString .= "\n"; # $contentString .= "\n"; foreach my $element (@embeddedClasses) { my $cName = $element->name(); # print STDERR "EMBEDDED CLASS: $cName\n"; my $cDesc = $element->discussion(); # my $uid = "//$apiUIDPrefix/c/econst/$cName"; # registerUID($uid); my $uid = $element->apiuid(); # "econst"); my $safeName = $cName; $safeName = &safeName(filename => $cName); my $url = $class_self->{PARSEDPSEUDOCLASSNAME}.$pathSeparator."Classes".$pathSeparator.$safeName.$pathSeparator."index.html"; my $target = "doc"; my $classAsComposite = $HeaderDoc::ClassAsComposite; # if ($class eq "HeaderDoc::Header") { $classAsComposite = 0; } if ($composite && !$classAsComposite) { $classAsComposite = 1; $target = "_top"; } if ($element->isAPIOwner()) { $target = "_top"; $classAsComposite = 0; } if ($HeaderDoc::use_iframes) { $target = "_top"; } # $contentString .= "\n"; $contentString .= "
    $cName
    $cDesc
    \n"; } # $contentString .= "
    NameDescription
    $cName$cDesc
    \n
    \n"; $contentString .= "\n\n"; } if (@constants) { $showDiscussionHeading = 1; $contentString .= "
    Constants
    \n"; $contentString .= "
    \n"; $contentString .= "
    \n"; # $contentString .= "\n"; # $contentString .= "\n"; foreach my $element (@constants) { my $cName = $element->name(); my $cDesc = $element->discussion(); # my $uid = "//$apiUIDPrefix/c/econst/$cName"; # registerUID($uid); my $uid = $element->apiuid(); # "econst"); # $contentString .= "\n"; if (!$apio->appleRefUsed($uid) && !$HeaderDoc::ignore_apiuid_errors) { # print STDERR "MARKING APIREF $uid used\n"; $apio->appleRefUsed($uid, 1); $contentString .= "
    $cName
    $cDesc
    \n"; } else { $contentString .= "
    $cName
    $cDesc
    \n"; } } # $contentString .= "
    NameDescription
    $cName$cDesc
    \n
    \n"; $contentString .= "\n\n"; } # print "IN $self LOCAL VARS: ".$#local_variables."\n"; if (scalar(@callbacks)) { $showDiscussionHeading = 1; $contentString .= "
    Callbacks
    \n"; $contentString .= "
    \n"; # $contentString .= "\n"; # $contentString .= "\n"; $contentString .= "
    "; # foreach my $element (@callbacks) { # print STDERR "ETYPE: $element->{TYPE}\n"; # } foreach my $element (@callbacks) { my $fName = $element->name(); my $fDesc = $element->discussion(); my $fType = $element->type(); if (($fType eq 'field') || ($fType eq 'constant') || ($fType eq 'funcPtr')){ # $contentString .= "
    \n"; $contentString .= "
    $fName
    $fDesc
    \n"; } elsif ($fType eq 'callback') { my @userDictArray = $element->userDictArray(); # contains elements that are hashes of param name to param doc my $paramString; foreach my $hashRef (@userDictArray) { while (my ($param, $disc) = each %{$hashRef}) { $paramString .= "
    $param
    \n
    $disc
    \n"; } if (length($paramString)) {$paramString = "
    \n".$paramString."\n
    \n";}; } # $contentString .= "\n"; $contentString .= "
    $fName
    $fDesc
    $paramString
    \n"; } else { my $fullpath = $HeaderDoc::headerObject->name(); if (!$HeaderDoc::ignore_apiuid_errors) { print STDERR "$fullpath:$linenum: warning: struct/typdef/union ($name) field with name $fName has unknown type: $fType\n"; # $element->printObject(); } } } # $contentString .= "
    NameDescription
    $fName$fDesc
    $fName$fDesc
    $paramString
    \n
    \n"; $contentString .= "\n\n"; } # if (length($desc)) {$contentString .= "

    $desc

    \n"; } # $contentString .= "
    "; # MOVED LOWER if (length($result)) { $showDiscussionHeading = 1; $contentString .= "
    Return Value

    "; # $contentString .= "$func_or_method result

    $contentString .= "$result\n"; $contentString .= "

    "; } my $stripdesc = $checkDisc; $stripdesc =~ s/
    /\n/sg; my $BD = ""; if ($self->can("blockDiscussion")) { $BD = $self->blockDiscussion(); $BD =~ s/
    /\n/sg; } # warn("DESC IS $desc\n"); # warn("stripdesc IS $stripdesc\n"); # warn("BD IS $BD\n"); if ($stripdesc =~ /\S/ || $BD =~ /\S/) { if ($showDiscussionHeading) { $contentString .= "
    Discussion
    \n"; } my $discstart = $self->headerDocMark("discussion", "start"); my $discend = $self->headerDocMark("discussion", "end"); $contentString .= ""; if ($self->can("isFramework") && $self->isFramework()) { $contentString .= "\n"; } else { $contentString .= $discstart; } $contentString .= $desc; if ($self->can("isFramework") && $self->isFramework()) { $contentString .= "\n"; } else { $contentString .= $discend; } $contentString .= "\n"; } # Local variables should be after the discussion. if (!$HeaderDoc::suppress_local_variables) { if (@local_variables) { $showDiscussionHeading = 1; $contentString .= "
    Local Variables
    \n"; # $contentString .= "\n"; # $contentString .= "\n"; my @groups = (); my %groupshash = (); foreach my $element (@local_variables) { my $group = $element->group(); $group =~ s/[\r\n]/ /sg; $group =~ s/^\s*//s; $group =~ s/\s*$//s; if (!$group) { $group = " "; } if (!$groupshash{$group}) { push(@groups, $group); my @arr = (); $groupshash{$group} = \@arr; } my @arr = @{$groupshash{$group}}; push(@arr, \$element); $groupshash{$group} = \@arr; } foreach my $group (@groups) { if ($group =~ /\S/) { $contentString .= "
    $group
    \n"; } $contentString .= "
    \n"; $contentString .= "
    \n"; # print STDERR "GROUP \"$group\"\n"; my @arr = @{$groupshash{$group}}; # print STDERR "COUNT: ".($#arr + 1)."\n"; foreach my $elementref (@arr) { my $element = ${$elementref}; # print STDERR "ELEMENT: $element\n"; my $cName = $element->name(); my $cDesc = $element->discussion(); # my $uid = "//$apiUIDPrefix/c/econst/$cName"; # registerUID($uid); my $uid = $element->apiuid(); # "econst"); # $contentString .= "
    \n"; if (!$apio->appleRefUsed($uid) && !$HeaderDoc::ignore_apiuid_errors) { # cluck("MARKING APIREF $uid used\n"); $apio->appleRefUsed($uid, 1); $contentString .= "
    $cName
    $cDesc
    \n"; } else { # cluck("Reused Apple Ref $uid\n"); $contentString .= "
    $cName
    $cDesc
    \n"; } } $contentString .= "\n\n"; } # $contentString .= "
    NameDescription
    $cName$cDesc
    \n\n"; } } # if (length($desc)) {$contentString .= "

    $desc

    \n"; } if (!$newTOCinAPIOwner) { # Otherwise we do this earlier. $contentString .= $attstring; } if (length($long_attributes)) { $contentString .= $long_attributes; } my $late_attributes = ""; if (length($namespace)) { $late_attributes .= "
    Namespace
    $namespace
    \n"; } if (length($availability)) { $late_attributes .= "
    Availability
    $availability
    \n"; } if (length($updated)) { $late_attributes .= "
    Updated:
    $updated
    \n"; } if (length($late_attributes)) { $contentString .= "
    ".$late_attributes."
    \n"; } # $contentString .= "
    \n"; # if ($HeaderDoc::enable_custom_references) { # $contentString .= "\n"; # end APIObject div # } my $value_fixed_contentString = $self->fixup_values($contentString); return $value_fixed_contentString; } # /*! # @abstract # Gets/sets the tagged parameters list for a function, etc. # @param self # This object # @param tplist # The new tagged parameters list to set. (Optional.) # @discussion # The tagged parameters list is an array of # {@link //apple_ref/perl/cl/HeaderDoc::MinorAPIElement MinorAPIElement} # objects that represent the parameters as tagged in the # HeaderDoc comment block. # */ sub taggedParameters { my $self = shift; if (@_) { @{ $self->{TAGGEDPARAMETERS} } = @_; } ($self->{TAGGEDPARAMETERS}) ? return @{ $self->{TAGGEDPARAMETERS} } : return (); } # /*! # @abstract # Returns a UID suitable for a composite page (when # classAsComposite is 0). # @param self # This object. # @discussion # Returns UIDs that begin with //apple_ref/doc/compositePage/ # (//apple_ref/doc/compositePage/c/func/myfuncname, for example). # */ sub compositePageUID { my $self = shift; my $uid = ""; if ($self->can("compositePageAPIUID")) { $uid = $self->compositePageAPIUID(); } else { my $apiUIDPrefix = HeaderDoc::APIOwner->apiUIDPrefix(); $uid = $self->apiuid(); $uid =~ s/\/\/\Q$apiUIDPrefix\E\//\/\/$apiUIDPrefix\/doc\/compositePage\//s; } # registerUID($uid); return $uid; } # /*! # @abstract # Adds a tagged parameter to the array of tagged parameters # associated with a function, etc. # @param self # The (generally Function) object. # @param taggedParms # An array of tagged parameters to add. # @discussion # The tagged parameters list is an array of # {@link //apple_ref/perl/cl/HeaderDoc::MinorAPIElement MinorAPIElement} # objects that represent the parameters as tagged in the # HeaderDoc comment block. # */ sub addTaggedParameter { my $self = shift; if (@_) { push (@{$self->{TAGGEDPARAMETERS}}, @_); my @arr = @{$self->{TAGGEDPARAMETERS}}; # print "OBJ IS ".\$arr[scalar(@arr) - 1]."\n"; # cluck("ADDED: ".\$arr[scalar(@arr) - 1]."\n"); return \$arr[scalar(@arr) - 1]; } return undef; # return @{ $self->{TAGGEDPARAMETERS} }; } # sub parsedParameters # { # # Override this in subclasses where relevant. # return (); # } # /*! # @abstract # Compares tagged parameters to parsed parameters (for validation) # @param self # This object. # */ sub taggedParsedCompare { my $self = shift; my @tagged = $self->taggedParameters(); my @parsed = $self->parsedParameters(); my $funcname = $self->name(); my $fullpath = $self->fullpath(); my $linenum = $self->linenum(); my $tpcDebug = 0; my $struct = 0; my $strict = $HeaderDoc::force_parameter_tagging; my %taggednames = (); my %parsednames = (); if ($self->{TPCDONE}) { return; } if (!$HeaderDoc::ignore_apiuid_errors) { $self->{TPCDONE} = 1; } my @fields = (); if ($self->can("fields")) { $struct = 1; @fields = $self->fields(); } my @constants = $self->constants(); my $apiOwner = $self->isAPIOwner(); if (!$self->suppressChildren()) { foreach my $myfield (@fields) { # $taggednames{$myfield} = $myfield; my $nscomp = $myfield->name(); $nscomp =~ s/\s*//sgo; $nscomp =~ s/^\**//sso; if (!length($nscomp)) { $nscomp = $myfield->type(); $nscomp =~ s/\s*//sgo; } $taggednames{$nscomp}=$myfield; print STDERR "Mapped Field $nscomp -> $myfield\n" if ($tpcDebug); } if (!$apiOwner) { foreach my $myconstant (@constants) { my $nscomp = $myconstant->name(); print STDERR "CONST: $nscomp\n" if ($tpcDebug); $nscomp =~ s/\s*//sgo; $nscomp =~ s/^\**//sso; if (!length($nscomp)) { $nscomp = $myconstant->type(); $nscomp =~ s/\s*//sgo; } $taggednames{$nscomp}=$myconstant; print STDERR "COUNT: ".(keys %taggednames)."\n" if ($tpcDebug); print STDERR "Mapped Constant $nscomp -> $myconstant\n" if ($tpcDebug); } } } foreach my $mytaggedparm (@tagged) { my $nscomp = $mytaggedparm->name(); $nscomp =~ s/\s*//sgo; $nscomp =~ s/^\**//sso; if (!length($nscomp)) { $nscomp = $mytaggedparm->type(); $nscomp =~ s/\s*//sgo; } $taggednames{$nscomp}=$mytaggedparm; print STDERR "Mapped Tagged Parm $nscomp -> $mytaggedparm\n" if ($tpcDebug); } if ($HeaderDoc::ignore_apiuid_errors) { # This avoids warnings generated by the need to # run documentationBlock once prior to the actual parse # to generate API references. if ($tpcDebug) { print STDERR "ignore_apiuid_errors set. Skipping tagged/parsed comparison.\n"; } # return; } # if ($self->lang() ne "C") { if ($self->lang() eq "perl" || $self->lang() eq "shell") { if ($tpcDebug) { print STDERR "Can't parse Perl or shell script parameter names.\nSkipping tagged/parsed comparison.\n"; } return; } if ($tpcDebug) { print STDERR "Tagged Parms:\n" if ($tpcDebug); foreach my $obj (@tagged) { bless($obj, "HeaderDoc::HeaderElement"); bless($obj, $obj->class()); print STDERR "TYPE: \"" .$obj->type . "\"\nNAME: \"" . $obj->name() ."\"\n"; } } print STDERR "Parsed Parms:\n" if ($tpcDebug); foreach my $obj (@parsed) { bless($obj, "HeaderDoc::HeaderElement"); bless($obj, $obj->class()); my $type = ""; if ($obj->can("type")) { $type = $obj->type(); } print STDERR "TYPE:" .$type . "\nNAME:\"" . $obj->name()."\"\n" if ($tpcDebug); my $nscomp = $obj->name(); $nscomp =~ s/\s*//sgo; $nscomp =~ s/^\**//sso; if (!length($nscomp)) { $nscomp = $type; $nscomp =~ s/\s*//sgo; } $parsednames{$nscomp}=$obj; } print STDERR "Checking Parameters and Stuff.\n" if ($tpcDebug); foreach my $taggedname (keys %taggednames) { my $searchname = $taggedname; my $tp = $taggednames{$taggedname}; if ($tp->type eq "funcPtr") { $searchname = $tp->name(); $searchname =~ s/\s*//sgo; } my $searchnameb = $searchname; $searchnameb =~ s/.*\.//s; # to allow tagging of subfields with the same name in a meaningful way. print STDERR "TN: $taggedname\n" if ($tpcDebug); print STDERR "SN: $searchname\n" if ($tpcDebug); if (!$parsednames{$searchname} && !$parsednames{$searchnameb}) { my $apio = $tp->apiOwner(); print STDERR "APIO: $apio SN: \"$searchname\"\n" if ($tpcDebug); my $tpname = $tp->type . " " . $tp->name(); $tpname =~ s/^\s*//s; my $oldfud = $self->{PPFIXUPDONE}; if (!$self->fixupParsedParameters($tp->name)) { if (!$oldfud) { # Fixup may have changed things. my @newparsed = $self->parsedParameters(); %parsednames = (); foreach my $obj (@newparsed) { bless($obj, "HeaderDoc::HeaderElement"); bless($obj, $obj->class()); print STDERR "TYPE:" .$obj->type . "\nNAME:" . $obj->name()."\n" if ($tpcDebug); my $nscomp = $obj->name(); $nscomp =~ s/\s*//sgo; $nscomp =~ s/^\**//sso; if (!length($nscomp)) { $nscomp = $obj->type(); $nscomp =~ s/\s*//sgo; } $parsednames{$nscomp}=$obj; } } if (!$HeaderDoc::ignore_apiuid_errors) { warn("$fullpath:$linenum: warning: Parameter $tpname does not appear in $funcname declaration ($self).\n"); print STDERR "---------------\n"; print STDERR "Candidates are:\n"; foreach my $ppiter (@parsed) { my $ppname = $ppiter->name(); if (!length($ppname)) { $ppname = $ppiter->type(); } print STDERR " \"".$ppname."\"\n"; } print STDERR "---------------\n"; } } } } if ($strict) { # && !$struct print STDERR "STRICT CHECK\n" if ($tpcDebug); foreach my $parsedname (keys %parsednames) { print STDERR "PN: $parsedname\n" if ($tpcDebug); if (!$taggednames{$parsedname}) { my $pp = $parsednames{$parsedname}; my $ppname = $pp->type . " " . $pp->name(); if (!$HeaderDoc::ignore_apiuid_errors) { warn("$fullpath:$linenum: warning: Parameter $ppname in $funcname declaration is not tagged.\n"); } elsif ($tpcDebug) { warn("Warning skipped\n"); } } } } } # /*! # @abstract # Searches for a parameter in the parsed parameters list. # Also takes additional parsed parameters from enclosed # structures and adds them to the outer typedef object. # @param self # This object. # @param name # A name to search for in the original parsed parameters list. # @result # Returns 1 if the specified name was found in the original # parsed parameters list, else 0. # */ sub fixupParsedParameters { my $self = shift; my $name = shift; # Only do this once per typedef. if ($self->{PPFIXUPDONE}) { return 0; } $self->{PPFIXUPDONE} = 1; my $retval = 0; my $simpleTDcontents = $self->typedefContents(); if (length($simpleTDcontents)) { my $addDebug = 0; $simpleTDcontents =~ s/\s+/ /sgo; $simpleTDcontents =~ s/^\s*//so; $simpleTDcontents =~ s/\s*$//so; my $origref = $HeaderDoc::namerefs{$simpleTDcontents}; if ($origref && ($origref != $self)) { print STDERR "Associating additional fields.\n" if ($addDebug); # print STDERR "ORIG: $origref\n"; bless($origref, "HeaderDoc::HeaderElement"); # print STDERR "ORIG: $origref\n"; bless($origref, $origref->class()); foreach my $origpp ($origref->parsedParameters()) { print STDERR "adding \"".$origpp->type()."\" \"".$origpp->name()."\" to $name\n" if ($addDebug); my $newpp = $origpp->clone(); $newpp->hidden(1); $self->addParsedParameter($newpp); if ($newpp->name() eq $name) { $retval = 1; } } } } return $retval; } # /*! # @abstract # Gets/sets the parsed parameters list for a function, etc. # @param self # This object # @param pplist # The new parsed parameters list to set. (Optional.) # @discussion # The parsed parameters list is an array of # {@link //apple_ref/perl/cl/HeaderDoc::MinorAPIElement MinorAPIElement} # objects that represent the parameters as parsed from the # actual declaration. Not all languages support parameter # name parsing. (For example, Perl does not.) # */ sub parsedParameters { my $self = shift; if (@_) { @{ $self->{PARSEDPARAMETERS} } = @_; } ($self->{PARSEDPARAMETERS}) ? return @{ $self->{PARSEDPARAMETERS} } : return (); } # /*! # @abstract # Adds a parsed parameter to the array of parsed parameters # associated with a function, etc. # @param self # The (generally Function) object. # @param parsedParam # An array of parsed parameters to add. # @discussion # The parsed parameters list is an array of # {@link //apple_ref/perl/cl/HeaderDoc::MinorAPIElement MinorAPIElement} # objects that represent the parameters as parsed from the # actual declaration. Not all languages support parameter # name parsing. (For example, Perl does not.) # */ sub addParsedParameter { my $self = shift; if (@_) { push (@{$self->{PARSEDPARAMETERS}}, @_); } return @{ $self->{PARSEDPARAMETERS} }; } # Drop the last parsed parameter. Used for rollback support. # /*! # @abstract # Deletes the last parsed parameter added to # this object. # @discussion # This is used when rolling things back while # handling a badly formed block declaration # (one that mixes functions and #defines, for # example). # */ sub dropParsedParameter { my $self = shift; my $last = pop(@{$self->{PARSEDPARAMETERS}}); # print STDERR "DROPPED $last\n"; # $last->dbprint(); return $last; } # for subclass/superclass merging # /*! # @abstract # Compares the parsed parameters of two methods. # @param self # This method object. # @param compareObj # The method to compare against. # @discussion # When merging methods in from the superclass, methods # that are overridden (methods with the same signature) # should not be included. This code is intended to # perform that check. # */ sub parsedParamCompare { my $self = shift; my $compareObj = shift; my @comparelist = $compareObj->parsedParameters(); my $name = $self->name(); my $localDebug = 0; my @params = $self->parsedParameters(); if (scalar(@params) != scalar(@comparelist)) { print STDERR "parsedParamCompare: function $name arg count differs (". scalar(@params)." != ". scalar(@comparelist) . ")\n" if ($localDebug); return 0; } # different number of args my $pos = 0; my $nparams = scalar(@params); while ($pos < $nparams) { my $compareparam = $comparelist[$pos]; my $param = $params[$pos]; if ($compareparam->type() ne $param->type()) { print STDERR "parsedParamCompare: function $name no match for argument " . $param->name() . ".\n" if ($localDebug); return 0; } $pos++; } print STDERR "parsedParamCompare: function $name matched.\n" if ($localDebug); return 1; } # /*! # @abstract # Gets/sets the return type for this object. # @param self # This object. # @param returntype # The new value. (Optional.) # */ sub returntype { my $self = shift; my $localDebug = 0; if (@_) { $self->{RETURNTYPE} = shift; print STDERR "$self: SET RETURN TYPE TO ".$self->{RETURNTYPE}."\n" if ($localDebug); } print STDERR "$self: RETURNING RETURN TYPE ".$self->{RETURNTYPE}."\n" if ($localDebug); return $self->{RETURNTYPE}; } # /*! # @abstract # Checks fr a tagged parameter matching a given name. # @param self # This object. # @param name # The parameter name to search for. # @result # Returns the parameter if it exists, else 0. # */ sub taggedParamMatching { my $self = shift; my $name = shift; my $localDebug = 0; return $self->paramMatching($name, \@{$self->{TAGGEDPARAMETERS}}); } # /*! # @abstract # Checks fr a parsed parameter matching a given name. # @param self # This object. # @param name # The parameter name to search for. # @result # Returns the parameter if it exists, else 0. # */ sub parsedParamMatching { my $self = shift; my $name = shift; my $localDebug = 0; return $self->paramMatching($name, \@{$self->{PARSEDPARAMETERS}}); } # /*! # @abstract # The guts of {@link taggedParamMatching} and {@link parsedParamMatching}. # @param self # This object. # @param name # The parameter name to search for. # @param arrayref # A reference to the array of parameters to search. # @result # Returns the parameter if it exists, else 0. # */ sub paramMatching { my $self = shift; my $name = shift; my $arrayref = shift; my @array = @{$arrayref}; my $localDebug = 0; print STDERR "SA: ".scalar(@array)."\n" if ($localDebug); # $HeaderDoc::count++; foreach my $param (@array) { my $reducedname = $name; my $reducedpname = $param->name; $reducedname =~ s/\W//sgo; $reducedpname =~ s/\W//sgo; print STDERR "comparing \"$reducedname\" to \"$reducedpname\"\n" if ($localDebug); if ($reducedname eq $reducedpname) { print STDERR "PARAM WAS $param\n" if ($localDebug); return $param; } } print STDERR "NO SUCH PARAM\n" if ($localDebug); return 0; } # /*! # @abstract # Returns the documentation output for this object (in XML). # @param self # This object. # */ sub XMLdocumentationBlock { my $self = shift; my $class = ref($self) || $self; my $compositePageString = ""; my $fullpath = $self->fullpath(); my $linenum = $self->linenum(); my $name = $self->textToXML($self->name(), 1, "$fullpath:$linenum:Name"); my $availability = $self->htmlToXML($self->availability(), 1, "$fullpath:$linenum:Availability"); my $updated = $self->htmlToXML($self->updated(), 1, "$fullpath:$linenum:Updated"); my $abstract = $self->htmlToXML($self->abstract(), 1, "$fullpath:$linenum:Abstract"); my $discussion = $self->htmlToXML($self->discussion(), 0, "$fullpath:$linenum:Discussion"); my $group = $self->htmlToXML($self->group(), 0, "$fullpath:$linenum:Group"); my $apio = $self->apiOwner(); my $apioclass = ref($apio) || $apio; my $contentString; my $localDebug = 0; my $type = ""; my $isAPIOwner = $self->isAPIOwner(); my $lang = $self->lang(); my $sublang = $self->sublang(); my $langstring = ""; my $fieldType = ""; my $fieldHeading = ""; my $uid = ""; my $fielduidtag = ""; my $extra = ""; my $accessControl = ""; if ($self->can("accessControl")) { $accessControl = $self->accessControl(); } if ($accessControl =~ /\S/) { $accessControl = " accessControl=\"$accessControl\""; } else { $accessControl = ""; } my $optionalOrRequired = ""; if ($self->parserState && ($apioclass =~ /HeaderDoc::ObjCProtocol/)) { $optionalOrRequired = $self->parserState->{optionalOrRequired}; if (length $optionalOrRequired) { $optionalOrRequired = " optionalOrRequired=\"$optionalOrRequired\""; } } my @embeddedClasses = (); my $class_self = undef; if ($self->lang eq "applescript" && $class eq "HeaderDoc::Function") { if (!$self->{ASCONTENTSPROCESSED}) { $class_self = $self->processAppleScriptFunctionContents(); # $class_self->dbprint(); my @classes = $class_self->classes(); foreach my $obj (@classes) { push(@embeddedClasses, $obj); } } } $langstring = $self->apiRefLanguage($sublang); # if ($sublang eq "cpp") { # $langstring = "cpp"; # } elsif ($sublang eq "C") { # $langstring = "c"; # } elsif ($lang eq "C") { # $langstring = "occ"; # } else { # # java, javascript, et al # $langstring = "$sublang"; # } my $defineinfo = ""; my @includedDefines = (); if ($self->{INCLUDED_DEFINES}) { @includedDefines = @{$self->{INCLUDED_DEFINES}}; } my @includedDefineUIDs = (); SWITCH: { ($class eq "HeaderDoc::Constant") && do { $fieldType = "field"; # this should never be needed $fieldHeading = "fieldlist"; # this should never be needed $type = "constant"; if ($apioclass eq "HeaderDoc::Header") { # global variable $uid = $self->apiuid("data"); } else { # class constant $uid = $self->apiuid("clconst"); } $isAPIOwner = 0; last SWITCH; }; ($class eq "HeaderDoc::CPPClass") && do { $fieldType = "field"; $fieldHeading = "template_fields"; # set the type for uid purposes $type = "cl"; if ($self->fields()) { $type = "tmplt"; } $uid = $self->apiuid("$type"); # set the type for xml tag purposes $type = "class"; if ($self->isCOMInterface()) { $type = "com_interface"; } $isAPIOwner = 1; last SWITCH; }; ($class eq "HeaderDoc::Header") && do { $fieldType = "field"; $fieldHeading = "fields"; my $filename = $self->filename(); my $fullpath = $self->fullpath(); # set the type for uid purposes $type = "header"; $uid = $self->apiuid("$type"); # set the type for xml tag purposes $type = "header"; $extra = " filename=\"$filename\" headerpath=\"$fullpath\""; if ($self->isFramework()) { $type = "framework"; } $isAPIOwner = 1; last SWITCH; }; ($class eq "HeaderDoc::Enum") && do { $fieldType = "constant"; $fieldHeading = "constantlist"; $type = "enum"; $uid = $self->apiuid("tag"); $fielduidtag = "econst"; $isAPIOwner = 0; last SWITCH; }; ($class eq "HeaderDoc::Function") && do { $fieldType = "parameter"; $fieldHeading = "parameterlist"; if ($apioclass eq "HeaderDoc::Header") { $type = "func"; } else { # if ($langstring eq "c") { # $type = "intfm"; # } else { # $type = "clm"; # } # $type = $apio->getMethodType($self->declaration) $type = $self->getMethodType(); } # if ($self->isTemplate()) { # $type = "ftmplt"; # } if ($apioclass eq "HeaderDoc::CPPClass") { my $paramSignature = $self->getParamSignature(); if (length($paramSignature)) { $paramSignature = "/$paramSignature"; # @@@ SIGNATURE appended here } if ($self->sublang() eq "C") { $paramSignature = ""; } if ($self->isTemplate()) { my $apiref = $self->apiref(0, "ftmplt", "$paramSignature"); } else { my $declarationRaw = $self->declaration(); # my $methodType = $apio->getMethodType($declarationRaw); my $methodType = $self->getMethodType(); my $apiref = $self->apiref(0, $methodType, "$paramSignature"); } $uid = $self->apiuid(); } else { $uid = $self->apiuid($type); } $type = "function"; $isAPIOwner = 0; last SWITCH; }; ($class eq "HeaderDoc::Method") && do { $fieldType = "parameter"; $fieldHeading = "parameterlist"; $type = "method"; my $declarationRaw = $self->declaration(); # my $methodType = $self->getMethodType($declarationRaw); my $methodType = $self->getMethodType(); $uid = $self->apiuid($methodType); $extra = " type=\"$methodType\""; $isAPIOwner = 0; last SWITCH; }; ($class eq "HeaderDoc::ObjCCategory") && do { $fieldType = "field"; $fieldHeading = "template_fields"; $type = "category"; $uid = $self->apiuid("cat"); $isAPIOwner = 1; last SWITCH; }; ($class eq "HeaderDoc::ObjCClass") && do { $fieldType = "field"; $fieldHeading = "template_fields"; $type = "class"; $uid = $self->apiuid("cl"); $isAPIOwner = 1; last SWITCH; }; ($class eq "HeaderDoc::ObjCContainer") && do { $fieldType = "field"; $fieldHeading = "template_fields"; $type = "class"; $uid = $self->apiuid("cl"); $isAPIOwner = 1; last SWITCH; }; ($class eq "HeaderDoc::ObjCProtocol") && do { $fieldType = "field"; $fieldHeading = "template_fields"; $type = "protocol"; $uid = $self->apiuid("intf"); $isAPIOwner = 1; last SWITCH; }; ($class eq "HeaderDoc::PDefine") && do { $fieldType = "parameter"; $fieldHeading = "parameterlist"; $type = "pdefine"; $uid = $self->apiuid("macro"); my $definetype = ""; if ($self->isBlock()) { $definetype = "block"; if (scalar(@includedDefines)) { foreach my $refobj (@includedDefines) { # $refobj->dbprint(); my $defineref = $refobj->{MAINOBJECT}; # Workaround for mixed blocks used in a # few I/O Kit bits where something is # defined as a #define in some cases # or as a function in others. This is # totally a case of "punting". if (!$defineref) { @includedDefines = (); @includedDefineUIDs = @{$self->autoRelate()}; } } } else { @includedDefines = (); @includedDefineUIDs = @{$self->autoRelate()}; } } elsif ($self->isFunctionLikeMacro()) { $definetype = "function"; } else { $definetype = "value"; } $defineinfo = "definetype=\"$definetype\" "; if ($self->parseOnly()) { $defineinfo .= "parseOnly=\"true\" "; } $isAPIOwner = 0; last SWITCH; }; ($class eq "HeaderDoc::Struct") && do { $fieldType = "field"; $fieldHeading = "fieldlist"; if ($self->isUnion()) { $type = "union"; } else { $type = "struct"; } $uid = $self->apiuid("tag"); $isAPIOwner = 0; last SWITCH; }; ($class eq "HeaderDoc::Typedef") && do { if ($self->isEnumList()) { $fieldType = "constant"; $fieldHeading = "constantlist"; } elsif ($self->isFunctionPointer()) { $fieldType = "parameter"; $fieldHeading = "parameterlist"; } else { $fieldType = "field"; $fieldHeading = "fieldlist"; } $type = "typedef"; $uid = $self->apiuid("tdef"); if ($self->isFunctionPointer()) { $extra = " type=\"funcPtr\""; } else { $extra = " type=\"simple\""; } $isAPIOwner = 0; last SWITCH; }; ($class eq "HeaderDoc::Var" || $class eq "HeaderDoc::MinorAPIElement") && do { # The @var pseudo-variables in Perl are MinorAPIElement objects. # Treat them like any other variable to the extent that it is # possible to do so. $fieldType = "field"; $fieldHeading = "fieldlist"; if ($self->can('isFunctionPointer')) { if ($self->isFunctionPointer()) { $fieldType = "parameter"; $fieldHeading = "parameterlist"; } } $type = "variable"; my $isProperty = $self->can('isProperty') ? $self->isProperty() : 0; my $typename = "data"; if ($isProperty) { $type = "property"; $typename = "instp"; } $uid = $self->apiuid($typename); $isAPIOwner = 0; last SWITCH; }; { warn "UNKNOWN CLASS $self in XMLdocumentationBlock\n"; warn "OBJECT: TYPE: $self NAME: ".$self->name()."\n"; warn "APIO: TYPE: $apio NAME: ".$apio->name()."\n"; }; } my $indexgroup = $self->indexgroup(); my $throws = $self->XMLthrows(); $compositePageString .= "<$type id=\"$uid\" $defineinfo"."lang=\"$langstring\"$extra$accessControl$optionalOrRequired>"; # e.g. ""; if (length($name)) { $compositePageString .= "$name\n"; } if ($indexgroup =~ /\S/) { $compositePageString .= "".textToXML($indexgroup).""; } if (length($abstract)) { $compositePageString .= "$abstract\n"; } if (length($availability)) { $compositePageString .= "$availability\n"; } if (length($updated)) { $compositePageString .= "$updated\n"; } if (length($group)) { $compositePageString .= "$group\n"; } my $value = ""; if ($self->can('value')) { $value = $self->value(); if (length($value) && ($value ne "UNKNOWN")) { # Always XML in this function, so do this every time. $value = $self->textToXML($value); $compositePageString .= "$value\n"; } } if (length($throws)) { $compositePageString .= "$throws\n"; } my @params = (); my @origfields = (); if ($self->can("fields")) { @origfields = $self->fields(); } if ($self->can("taggedParameters")){ print STDERR "setting params\n" if ($localDebug); @params = $self->taggedParameters(); if ($self->can("parsedParameters")) { $self->taggedParsedCompare(); } } elsif ($self->can("fields")) { if ($self->can("parsedParameters")) { $self->taggedParsedCompare(); } } else { print STDERR "type $class has no taggedParameters function\n" if ($localDebug); } my @parsedparams = (); if ($self->can("parsedParameters")) { @parsedparams = $self->parsedParameters(); } my @parameters_or_fields = (); my @callbacks = (); foreach my $element (@params) { if ($element->isCallback()) { push(@callbacks, $element); } elsif (!$element->{ISDEFINE}) { push(@parameters_or_fields, $element); } } foreach my $element (@origfields) { bless($element, "HeaderDoc::HeaderElement"); bless($element, $element->class()); # MinorAPIElement"); if ($element->can("hidden")) { if (!$element->hidden()) { if ($element->isCallback()) { push(@callbacks, $element); } elsif (!$element->{ISDEFINE}) { push(@parameters_or_fields, $element); } } } } my @orig_local_variables = $self->variables(); my @origconstants = $self->constants(); my @local_variables = (); my @constants = (); # my @fields = (); # foreach my $copyfield (@origfields) { # bless($copyfield, "HeaderDoc::HeaderElement"); # bless($copyfield, $copyfield->class()); # MinorAPIElement"); # # print STDERR "FIELD: ".$copyfield->name."\n"; # if ($copyfield->can("hidden")) { # if (!$copyfield->hidden()) { # push(@fields, $copyfield); # } # } # } foreach my $copylocal (@orig_local_variables) { bless($copylocal, "HeaderDoc::HeaderElement"); bless($copylocal, $copylocal->class()); # MinorAPIElement"); # print STDERR "CONST: ".$copylocal->name."\n"; if ($copylocal->can("hidden")) { if (!$copylocal->hidden()) { push(@local_variables, $copylocal); } } # print STDERR "HIDDEN: ".$copylocal->hidden()."\n"; } foreach my $copyconstant (@origconstants) { bless($copyconstant, "HeaderDoc::HeaderElement"); bless($copyconstant, $copyconstant->class()); # MinorAPIElement"); # print STDERR "CONST: ".$copyconstant->name."\n"; if ($copyconstant->can("hidden")) { if (!$copyconstant->hidden()) { push(@constants, $copyconstant); } } # print STDERR "HIDDEN: ".$copyconstant->hidden()."\n"; } # if (@parameters_or_fields) { # $contentString .= "<$fieldHeading>\n"; # for my $field (@parameters_or_fields) { # my $name = $field->name(); # my $desc = $field->discussion(); # # print STDERR "field $name $desc\n"; # $contentString .= "<$fieldType>$name$desc\n"; # } # $contentString .= "\n"; # } # Insert declaration, fields, constants, etc. my $parseTree_ref = $self->parseTree(); my $parseTree = undef; # Don't do anything for @var pseudo-variables in Perl, etc. if ($class ne "HeaderDoc::MinorAPIElement") { if (!$parseTree_ref) { if (!$parseTree_ref && !$self->isAPIOwner()) { warn "Missing parse tree for ".$self->name()."\n"; } } else { $parseTree = ${$parseTree_ref}; } } my $declaration = ""; if ($parseTree) { # $declaration = $parseTree->xmlTree($self->preserve_spaces(), $self->hideContents()); $declaration = $self->declarationInHTML(); } if (@constants) { $compositePageString .= "\n"; foreach my $field (@constants) { my $name = $self->textToXML($field->name()); my $desc = $self->htmlToXML($field->discussion()); my $fType = ""; if ($field->can("type")) { $fType = $field->type(); } my $fielduidstring = ""; if (length($fielduidtag)) { my $fielduid = $field->apiuid($fielduidtag); $fielduidstring = " id=\"$fielduid\""; if (!$apio->appleRefUsed($uid) && !$HeaderDoc::ignore_apiuid_errors) { # print STDERR "MARKING APIREF $uid used\n"; $apio->appleRefUsed($uid, 1); } else { # already used or a "junk" run to obtain # uids for another purpose. Drop the # uid in case it is already used $fielduidstring = ""; } } if ($fType eq "callback") { my @userDictArray = $field->userDictArray(); # contains elements that are hashes of param name to param doc my $paramString; foreach my $hashRef (@userDictArray) { while (my ($param, $disc) = each %{$hashRef}) { $param = $self->textToXML($param); $disc = $self->htmlToXML($disc); $paramString .= "$param$disc\n"; } $compositePageString .= "$name$desc$paramString\n"; } } else { $compositePageString .= "$name$desc\n"; } } $compositePageString .= "\n"; } if (@embeddedClasses) { $contentString = $class_self->_getEmbeddedClassXMLDetailString(\@embeddedClasses); if (length($contentString)) { $compositePageString .= "\n"; $compositePageString .= $contentString; $compositePageString .= "\n"; } } if (@local_variables) { $compositePageString .= "\n"; foreach my $field (@local_variables) { my $name = $self->textToXML($field->name()); my $desc = $self->htmlToXML($field->discussion()); my $fType = ""; if ($field->can("type")) { $fType = $field->type(); } my $groupstring = ""; my $groupraw = $field->group(); if (length($groupraw)) { $groupraw =~ s/"/\"/sg; $groupstring = "".textToXML($groupraw).""; } my $fielduidstring = ""; if (length($fielduidtag)) { my $fielduid = $field->apiuid($fielduidtag); $fielduidstring = " id=\"$fielduid\""; if (!$apio->appleRefUsed($uid) && !$HeaderDoc::ignore_apiuid_errors) { # print STDERR "MARKING APIREF $uid used\n"; $apio->appleRefUsed($uid, 1); } else { # already used or a "junk" run to obtain # uids for another purpose. Drop the # uid in case it is already used $fielduidstring = ""; } } if ($fType eq "callback") { my @userDictArray = $field->userDictArray(); # contains elements that are hashes of param name to param doc my $paramString; foreach my $hashRef (@userDictArray) { while (my ($param, $disc) = each %{$hashRef}) { $param = $self->textToXML($param); $disc = $self->htmlToXML($disc); $paramString .= "$param$disc\n"; } $compositePageString .= "$name$desc$paramString$groupstring\n"; } } else { $compositePageString .= "$name$desc\n"; } } $compositePageString .= "\n"; } if (@parameters_or_fields) { $compositePageString .= "<$fieldHeading>\n"; foreach my $field (@parameters_or_fields) { my $name = $self->textToXML($field->name()); my $desc = $self->htmlToXML($field->discussion()); my $fType = ""; if ($field->can("type")) { $fType = $field->type(); } if ($fType eq "callback") { my @userDictArray = $field->userDictArray(); # contains elements that are hashes of param name to param doc my $paramString; foreach my $hashRef (@userDictArray) { while (my ($param, $disc) = each %{$hashRef}) { $param = $self->textToXML($param); $disc = $self->htmlToXML($disc); $paramString .= "$param$disc\n"; } $compositePageString .= "<$fieldType>$name$desc$paramString\n"; } } else { $compositePageString .= "<$fieldType>$name$desc\n"; } } $compositePageString .= "\n"; } if (@callbacks) { $compositePageString .= "\n"; foreach my $field (@callbacks) { my $name = $self->textToXML($field->name()); my $desc = $self->htmlToXML($field->discussion()); my $fType = ""; if ($field->can("type")) { $fType = $field->type(); } if ($fType eq "callback") { my @userDictArray = $field->userDictArray(); # contains elements that are hashes of param name to param doc my $paramString; foreach my $hashRef (@userDictArray) { while (my ($param, $disc) = each %{$hashRef}) { $param = $self->textToXML($param); $disc = $self->htmlToXML($disc); $paramString .= "$param$disc\n"; } $compositePageString .= "<$fieldType>$name$desc$paramString\n"; } } else { $compositePageString .= "<$fieldType>$name$desc\n"; } } $compositePageString .= "\n"; } if (scalar(@parsedparams) && (!$self->isBlock())) { # PDefine blocks use parsed parameters to store all of the defines # in a define block, so this would be bad. my $paramContentString; foreach my $element (@parsedparams) { my $pName = $self->textToXML($element->name()); # if (!$element->can("type")) { # cluck("ELEMENT TRACE: ".$element." (".$element->name().") in $self (".$self->name().") in ".$self->apiOwner()." (".$self->apiOwner()->name().")\n"); # my $headerObj = $HeaderDoc::headerObject; # $headerObj->headerDump(); # next; # } my $pType = $self->textToXML($element->type()); $pType =~ s/\s*$//so; if ($pName =~ s/^\s*(\*+)\s*//so) { $pType .= " $1"; } $pType = $self->textToXML($pType); $pName = $self->textToXML($pName); if (length ($pName) || length($pType)) { $paramContentString .= "$pType$pName\n"; } } if (length ($paramContentString)){ $compositePageString .= "\n"; $compositePageString .= $paramContentString; $compositePageString .= "\n"; } } my $returntype = $self->textToXML($self->returntype()); my $result = ""; if ($self->can('result')) { $result = html2xhtml($self->result(), $self->encoding()); } my $attlists = ""; if ($self->can('getAttributeLists')) { $attlists = $self->getAttributeLists(0); } my $atts = ""; if ($self->can('getAttributes')) { $atts = $self->getAttributes(); } if (length($atts)) { $compositePageString .= "$atts\n"; } if (length($attlists)) { $compositePageString .= "$attlists\n"; } my $idstring = ""; if (scalar(@includedDefines)) { foreach my $refobj (@includedDefines) { # $refobj->dbprint(); my $defineref = $refobj->{MAINOBJECT}; if (!$defineref) { next; } my $define = ${$defineref}; $idstring .= "".$define->apiuid()."\n"; # $refobj->dbprint(); # $define->dbprint(); # die("Define: $define\n"); } } if (scalar(@includedDefineUIDs)) { foreach my $idUID (@includedDefineUIDs) { $idstring .= "".$idUID."\n"; } } if (length($idstring)) { $compositePageString .= "\n$idstring\n\n"; } if ($class eq "HeaderDoc::Header") { my $includeref = $HeaderDoc::perHeaderIncludes{$fullpath}; if ($includeref) { my @includes = @{$includeref}; $compositePageString .= "\n"; foreach my $include (@includes) { print STDERR "Included file: $include\n" if ($localDebug); my $xmlinc = $self->textToXML($include); # $compositePageString .= "$xmlinc\n"; my $includeguts = $include; $includeguts =~ s/[<\"](.*)[>\"]/$1/so; my $includefile = basename($includeguts); my $ref = $self->genRefSub("doc", "header", $includefile, ""); $compositePageString .= "$xmlinc\n"; } $compositePageString .= "\n"; } } if (length($returntype)) { $compositePageString .= "$returntype\n"; } if (length($result)) { $compositePageString .= "$result\n"; } if (length($declaration)) { $compositePageString .= "$declaration\n"; } if (length($discussion)) { $compositePageString .= "$discussion\n"; } my @autoRelated = @{$self->autoRelate()}; if (scalar(@autoRelated)) { $compositePageString .= "\n"; foreach my $autoRelatedItem (@autoRelated) { $compositePageString .= "$autoRelatedItem\n"; } $compositePageString .= "\n"; } # Quick debugging of discussion and abstract # print STDERR "OBJ $self\n"; # print STDERR " NAME: ".$self->name()."\n"; # print STDERR " DESC: ".$discussion."\n"; # print STDERR " ABS: ".$abstract."\n"; if ($isAPIOwner) { $compositePageString .= $self->groupDoc(); $contentString = $self->_getFunctionXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $compositePageString .= "$contentString\n"; } $contentString= $self->_getMethodXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $compositePageString .= "$contentString\n"; } $contentString= $self->_getPropXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $compositePageString .= "$contentString\n"; } $contentString= $self->_getVarXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $compositePageString .= "$contentString\n"; } $contentString= $self->_getConstantXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $compositePageString .= "$contentString\n"; } $contentString= $self->_getTypedefXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $compositePageString .= "$contentString"; } $contentString= $self->_getStructXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $compositePageString .= "$contentString"; } $contentString= $self->_getEnumXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $compositePageString .= "$contentString"; } $contentString= $self->_getPDefineXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $compositePageString .= "$contentString"; } # @@@ Class generation code. Important debug checkpoint. my $classContent = ""; $contentString= $self->_getClassXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $classContent .= $contentString; } $contentString= $self->_getCategoryXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $classContent .= $contentString; } $contentString= $self->_getProtocolXMLDetailString(); if (length($contentString)) { $contentString = $self->stripAppleRefs($contentString); $classContent .= $contentString; } if (length($classContent)) { $compositePageString .= "$classContent\n"; } } if ($isAPIOwner) { my $copyrightOwner = $self->copyrightOwner; if ($class eq "HeaderDoc::Header") { my $headercopyright = $self->htmlToXML($self->headerCopyrightOwner()); if ($headercopyright ne "") { $copyrightOwner = $headercopyright; } } $compositePageString .= "© $copyrightOwner\n" if (length($copyrightOwner)); my $dateStamp = HeaderDoc::APIOwner::fix_date($self->encoding()); $compositePageString .= "$dateStamp\n" if (length($dateStamp)); } $compositePageString .= ""; # e.g. ""; return $compositePageString; } # /*! # @abstract # Gets/sets whether this object is a function pointer. # @param self # This object. # @param ifp # The new value. (Optional.) # */ sub isFunctionPointer { my $self = shift; if (@_) { $self->{ISFUNCPTR} = shift; # cluck($self->{NAME}." ($self) IFP SET TO ".$self->{ISFUNCPTR}); } return $self->{ISFUNCPTR}; } # /*! # @abstract # Adds a #define to the array of # #define macros associated with a # defineblock declaration. # @param self # The (generally {@link //apple_ref/perl/cl/HeaderDoc::PDefine PDefine}) object. # @param defines # An array of macros to add. # */ sub addToIncludedDefines { my $self = shift; my $obj = shift; if (!$self->{INCLUDED_DEFINES}) { my @x = (); $self->{INCLUDED_DEFINES} = \@x; } push(@{$self->{INCLUDED_DEFINES}}, $obj); my @arr = @{$self->{INCLUDED_DEFINES}}; # print "OBJ IS ".\$arr[scalar(@arr) - 1]."\n"; return \$arr[scalar(@arr) - 1]; } # /*! # @abstract # Collects data for generating the API ref (apple_ref) for a function, data type, etc. # @discussion # See {@link apiref} for the actual generation. # */ sub apirefSetup { my $self = shift; my $force = 0; my $localDebug = 0; if (@_) { $force = shift; } # warn("APIRS ($self) $force BT\n"); if ($self->noRegisterUID()) { print STDERR "SHORTCUT NOREGISTERUID: $self\n" if ($localDebug); return ($self->{KEEPCONSTANTS}, $self->{KEEPFIELDS}, $self->{KEEPPARAMS}, $self->{FIELDHEADING}, $self->{FUNCORMETHOD}, $self->{KEEPVARIABLES}); } my $subreftitle = 0; if ($self->appleRefIsDoc() == 1) { $subreftitle = 1; } # print STDERR "OBJ: $self NAME: ".$self->name()." SRT: $subreftitle\n"; my $class = ref($self) || $self; my $apio = $self->apiOwner(); my $apioclass = ref($apio) || $apio; my $declarationRaw = $self->declaration(); my @orig_local_variables = $self->variables(); my @origconstants = $self->constants(); my @origfields = (); my @params = (); my $apiref = ""; my $typename = ""; my $fieldHeading = ""; my $className = ""; my $apiRefType = ""; my $func_or_method = ""; print "APIREFSETUP: IN $self LOCAL VARS: ".$#orig_local_variables."\n" if($localDebug); if ($self->can("taggedParameters")){ print STDERR "setting params\n" if ($localDebug); @params = $self->taggedParameters(); if ($self->can("parsedParameters")) { $self->taggedParsedCompare(); } } elsif ($self->can("fields")) { if ($self->can("parsedParameters")) { $self->taggedParsedCompare(); } } else { print STDERR "type $class has no taggedParameters function\n" if ($localDebug); } if (!$force && $self->{APIREFSETUPDONE}) { print STDERR "SHORTCUT: $self\n" if ($localDebug); return ($self->{KEEPCONSTANTS}, $self->{KEEPFIELDS}, $self->{KEEPPARAMS}, $self->{FIELDHEADING}, $self->{FUNCORMETHOD}, $self->{KEEPVARIABLES}); } if ($self->{APIREFSETUPDONE}) { if (dereferenceUIDObject($self->{APIUID}, $self)) { unregister_force_uid_clear($self->{APIUID}); } } # print STDERR "REDO: $self\n"; if ($self->can("fields")) { @origfields = $self->fields(); } # my @constants = @origconstants; # my @fields = @origfields; my @constants = (); my @local_variables = (); my @fields = (); if (!$self->suppressChildren()) { foreach my $copyfield (@origfields) { bless($copyfield, "HeaderDoc::HeaderElement"); bless($copyfield, $copyfield->class()); # MinorAPIElement"); print STDERR "FIELD: ".$copyfield->name."\n" if ($localDebug); if ($copyfield->can("hidden")) { if (!$copyfield->hidden()) { push(@fields, $copyfield); } else { print STDERR "HIDDEN\n" if ($localDebug); } } } foreach my $copyconstant (@origconstants) { # print STDERR "CONSTANT IN SETUP\n"; bless($copyconstant, "HeaderDoc::HeaderElement"); bless($copyconstant, $copyconstant->class()); # MinorAPIElement"); # print STDERR "CONST: ".$copyconstant->name."\n"; if ($copyconstant->can("hidden")) { if (!$copyconstant->hidden()) { push(@constants, $copyconstant); } } # print STDERR "HIDDEN: ".$copyconstant->hidden()."\n"; } # print STDERR "SELF WAS $self\n"; } else { print STDERR "CHILDREN SUPPRESSED\n" if ($localDebug); } $typename = "internal_temporary_object"; SWITCH: { ($class eq "HeaderDoc::Function") && do { print STDERR "FUNCTION\n" if ($localDebug); $typename = $self->getMethodType(); # if ($apioclass eq "HeaderDoc::Header") { # $typename = "func"; # } else { # $typename = "clm"; # if ($apio->can("getMethodType")) { # $typename = $apio->getMethodType($self->declaration); # } # } print STDERR "Function type: $typename\n" if ($localDebug); # if ($self->isTemplate()) { # $typename = "ftmplt"; # } if ($apioclass eq "HeaderDoc::CPPClass") { my $paramSignature = $self->getParamSignature(); print STDERR "paramSignature: $paramSignature\n" if ($localDebug); if (length($paramSignature)) { $paramSignature = "/$paramSignature"; # @@@SIGNATURE appended here } if ($self->sublang() eq "C") { $paramSignature = ""; } if ($self->isTemplate()) { $apiref = $self->apiref(0, "ftmplt", "$paramSignature"); } else { my $declarationRaw = $self->declaration(); # my $methodType = $apio->getMethodType($declarationRaw); my $methodType = $self->getMethodType(); $apiref = $self->apiref(0, $methodType, "$paramSignature"); } } $fieldHeading = "Parameters"; $apiRefType = ""; $func_or_method = "function"; last SWITCH; }; ($class eq "HeaderDoc::Constant") && do { print STDERR "CONSTANT\n" if ($localDebug); if ($apioclass eq "HeaderDoc::Header") { $typename = "data"; } else { $typename = "clconst"; } $fieldHeading = "Fields"; $apiRefType = ""; last SWITCH; }; ($class eq "HeaderDoc::Enum") && do { print STDERR "ENUM\n" if ($localDebug); $typename = "tag"; $fieldHeading = "Constants"; # if ($self->masterEnum()) { $apiRefType = "econst"; # } else { # $apiRefType = ""; # } last SWITCH; }; ($class eq "HeaderDoc::PDefine") && do { print STDERR "PDEFINE\n" if ($localDebug); $typename = "macro"; $fieldHeading = "Parameters"; $apiRefType = ""; last SWITCH; }; ($class eq "HeaderDoc::Method") && do { print STDERR "METHOD\n" if ($localDebug); # $typename = $self->getMethodType($declarationRaw); $typename = $self->getMethodType(); $fieldHeading = "Parameters"; $apiRefType = ""; if ($apio->can("className")) { # to get the class name from Category objects $className = $apio->className(); } else { $className = $apio->name(); } $func_or_method = "method"; last SWITCH; }; ($class eq "HeaderDoc::Struct") && do { print STDERR "TAG\n" if ($localDebug); $typename = "tag"; $fieldHeading = "Fields"; $apiRefType = ""; last SWITCH; }; ($class eq "HeaderDoc::Typedef") && do { print STDERR "TDEF\n" if ($localDebug); $typename = "tdef"; if ($self->isFunctionPointer()) { $fieldHeading = "Parameters"; last SWITCH; } if ($self->isEnumList()) { $fieldHeading = "Constants"; last SWITCH; } $fieldHeading = "Fields"; $apiRefType = ""; $func_or_method = "function"; last SWITCH; }; ($class eq "HeaderDoc::Var" || ($class eq "HeaderDoc::MinorAPIElement" && $self->autodeclaration())) && do { print STDERR "VAR\n" if ($localDebug); my $isProperty = $self->can('isProperty') ? $self->isProperty() : 0; if ($isProperty) { $typename = "instp"; } else { $typename = "data"; } $fieldHeading = "Fields"; if ($self->can('isFunctionPointer')) { if ($self->isFunctionPointer()) { $fieldHeading = "Parameters"; } } $apiRefType = ""; last SWITCH; }; } if (!length($apiref)) { # cluck( "TYPE NAME: $typename CLASS: $class\n"); $apiref = $self->apiref(0, $typename); } if (@constants) { foreach my $element (@constants) { $element->appleRefIsDoc($subreftitle); my $uid = $element->apiuid("econst"); } } if (@params) { foreach my $element (@params) { if (length($apiRefType)) { # print STDERR "APIREFTYPE: $apiRefType\n"; $element->appleRefIsDoc($subreftitle); $apiref = $element->apiref(0, $apiRefType); } } } # print STDERR "OLV: ".$#orig_local_variables."\n"; foreach my $copylocal (@orig_local_variables) { bless($copylocal, "HeaderDoc::HeaderElement"); bless($copylocal, $copylocal->class()); # MinorAPIElement"); print STDERR "LOCAL VARIABLE: ".$copylocal->name."\n" if ($localDebug); if (length($apiRefType)) { # print STDERR "APIREFTYPE: $apiRefType\n"; $copylocal->appleRefIsDoc($subreftitle); $apiref = $copylocal->apiref(0, $apiRefType); } if ($copylocal->can("hidden")) { if (!$copylocal->hidden()) { push(@local_variables, $copylocal); } } # print STDERR "HIDDEN: ".$copylocal->hidden()."\n"; } $self->{KEEPVARIABLES} = \@local_variables; $self->{KEEPCONSTANTS} = \@constants; $self->{KEEPFIELDS} = \@fields; $self->{KEEPPARAMS} = \@params; $self->{FIELDHEADING} = $fieldHeading; $self->{FUNCORMETHOD} = $func_or_method; $self->{APIREFSETUPDONE} = 1; return (\@constants, \@fields, \@params, $fieldHeading, $func_or_method, \@local_variables); } # /*! @abstract # Returns HeaderDoc tag names and English names for a given HeaderDoc object. # @param self # This object. # @discussion # Returns an array with three members: the English name, a # regular expression to match the tag names that make sense # for this object, and (if applicable) the superclass field name. # # For example, an instance of the # {@link //apple_ref/perl/cl/HeaderDoc::Function HeaderDoc::Function} class # returns an english name of function or method, and returns # a regular expression that matches either the function or # method HeaderDoc tags. Because it cannot have a superclass, # it doesn't need to return anything for the third field. # */ sub tagNameRegexpAndSuperclassFieldNameForType { my $self = shift; my $class = ref($self) || $self; my $tagname = ""; my $tag_re = ""; my $superclassfieldname = "Superclass"; SWITCH: { ($class =~ /HeaderDoc\:\:Constant/) && do { $tag_re = "const(?:ant)?|var"; $tagname = "constant"; last SWITCH; }; ($class =~ /HeaderDoc\:\:Enum/) && do { $tag_re = "enum"; $tagname = "enum"; last SWITCH; }; ($class =~ /HeaderDoc\:\:Function/) && do { $tag_re = "method|function"; $tagname = "function or method"; last SWITCH; }; ($class =~ /HeaderDoc\:\:Method/) && do { $tag_re = "method"; $tagname = "method"; last SWITCH; }; ($class =~ /HeaderDoc\:\:PDefine/) && do { $tag_re = "define(?:d)?|function"; $tagname = "CPP macro"; last SWITCH; }; ($class =~ /HeaderDoc\:\:Struct/) && do { $tag_re = "struct|union"; if ($self->isUnion()) { $tagname = "union"; } else { $tagname = "struct"; } last SWITCH; }; ($class =~ /HeaderDoc\:\:Typedef/) && do { $tag_re = "typedef|function|class"; $tagname = "typedef"; last SWITCH; }; ($class =~ /HeaderDoc\:\:Var/) && do { if ($self->isProperty()) { $tag_re = "var|method|property|const(?:ant)?"; $tagname = "variable"; } else { $tag_re = "var|property|const(?:ant)?"; $tagname = "variable"; } last SWITCH; }; ($class =~ /HeaderDoc\:\:ObjCClass/) && do { $tag_re = "class|template"; $tagname = "Objective-C class"; $superclassfieldname = "Extends Class"; last SWITCH; }; ($class =~ /HeaderDoc\:\:ObjCCategory/) && do { $tag_re = "category|template"; $tagname = "Objectice-C category"; $superclassfieldname = "Extends Class"; last SWITCH; }; ($class =~ /HeaderDoc\:\:ObjCProtocol/) && do { $tag_re = "protocol|template"; $tagname = "Objectice-C protocol"; $superclassfieldname = "Extends Protocol"; last SWITCH; }; ($class =~ /HeaderDoc\:\:CPPClass/) && do { $tag_re = "class|interface"; $tagname = "class or interface"; last SWITCH; }; { print STDERR "Unknown type: $class\n"; } }; return ($tagname, $tag_re, $superclassfieldname); } # /*! # @abstract # Finds a tagged parameter by name. # @discussion # To improve Doxygen tag compatibility, HeaderDoc # concatenates multiple \@param tags for the # same parameter name. This is part of that # support. # */ sub taggedParamFind { my $self = shift; my $name = shift; foreach my $parm ($self->taggedParameters()) { if ($parm->name() eq $name) { return $parm; } } return undef; } # /*! # @abstract # Processes a HeaderDoc comment. # @param self # The APIOwner object. # @param fieldArrayRef # A reference to an array of fields. This should be the result of calling # {@link //apple_ref/perl/instm/HeaderDoc::Utilities/stringToFields//() # HeaderDoc::Utilities::stringToFields} on the HeaderDoc comment. # # This is essentially the result of calling split on the \@ symbol # in the comment, but there is some subtlety involved, so don't do that. # */ sub processComment { my($self) = shift; my $fieldArrayRef = shift; my @fields = @$fieldArrayRef; my $fullpath = $self->fullpath(); my $linenuminblock = $self->linenuminblock(); my $blockoffset = $self->blockoffset(); my $linenum = $self->linenum(); my $localDebug = 0; my $olddisc = $self->halfbaked_discussion(); my $isProperty = $self->can('isProperty') ? $self->isProperty() : 0; my $vargroup = ""; if ($localDebug) { printFields($fieldArrayRef); cluck("BT\n"); } print STDERR "SELF IS $self\n" if ($localDebug); # cluck("Entering processComment for $self\n"); # printFields(\@fields); my $lang = $self->lang(); my $sublang = $self->sublang(); my $lastField = scalar(@fields); # warn "processComment called on raw HeaderElement\n"; my $class = ref($self) || $self; if ($class =~ /HeaderDoc::HeaderElement/) { return; } $self->{HASPROCESSEDCOMMENT} = 1; my ($tagname, $tag_re, $superclassfieldname) = $self->tagNameRegexpAndSuperclassFieldNameForType(); my $seen_top_level_field = 0; my $first_field = 1; my $callbackObj = 0; foreach my $field (@fields) { print STDERR "Comment field is |$field|\n" if ($localDebug); print STDERR "Seen top level field: $seen_top_level_field\n" if ($localDebug); my $fieldname = ""; my $top_level_field = 0; if ($field =~ /^(\w+)(\s|$)/) { $fieldname = $1; # print STDERR "FIELDNAME: $fieldname\n"; $top_level_field = validTag($fieldname, 1); if ($top_level_field && $seen_top_level_field && ($fieldname !~ /const(ant)?/) && ($fieldname !~ /var/) && (!$self->isBlock() || $fieldname ne "define") && $fieldname ne "name") { # We've seen more than one top level field. $field =~ s/^(\w+)(\s)//s; my $spc = $2; my $newtag = "field"; if ($class =~ /HeaderDoc\:\:Enum/) { $newtag = "constant"; } elsif ($class =~ /HeaderDoc\:\:Function/ || $class =~ /HeaderDoc\:\:Method/) { $newtag = "param"; } elsif ($class =~ /HeaderDoc\:\:ObjCClass/ || $class =~ /HeaderDoc\:\:ObjCProtocol/ || $class =~ /HeaderDoc\:\:ObjCCategory/ || $class =~ /HeaderDoc\:\:ObjCContainer/ || $class =~ /HeaderDoc\:\:CPPClass/) { $newtag = "discussion"; } elsif ($class =~ /HeaderDoc\:\:Typedef/) { if ($fieldname eq "function") { $newtag = "callback"; } elsif ($fieldname =~ /define(d)?/ || $fieldname eq "var") { $newtag = "constant"; } } $field = "$newtag$spc$field"; $top_level_field = 0; warn "$fullpath:$linenum: Duplicate top level tag \"$fieldname\" detected\n". "in comment. Maybe you meant \"$newtag\".\n"; # warn "Thunked field to \"$field\"\n"; } elsif ($top_level_field && $seen_top_level_field && ($fieldname eq "constant" || $fieldname eq "const")) { $top_level_field = 0; } elsif ($top_level_field && $seen_top_level_field && ($fieldname eq "var")) { $top_level_field = 0; } # Fix for another common mistake: people using @field for a class # member variable instead of @var or @const. if ($class =~ /HeaderDoc\:\:Var/ && !$isProperty) { if (!$seen_top_level_field && $fieldname eq "field") { if ($sublang !~ /^occ/o) { warn "Field \@$fieldname found in \@var declaration.\nYou probably meant \@var instead.\n" if (!$HeaderDoc::running_test); $field =~ s/^(\w+)(\s)//s; my $spc = $2; $field = "var$spc$field"; $top_level_field = 1; } } } } # warn("FN: \"$fieldname\"\n"); # print STDERR "TLF: $top_level_field, FN: \"$fieldname\"\n"; # print STDERR "FIELD $field\n"; SWITCH: { ($field =~ /^\/(\*|\/)\!/o && $first_field) && do { my $copy = $field; $copy =~ s/^\/(\*|\/)\!\s*//s; if (length($copy)) { $self->discussion($copy); $seen_top_level_field = 1; } last SWITCH; }; # (($lang eq "java") && ($field =~ /^\s*\/\*\*/o)) && do { # ignore opening /** # last SWITCH; # }; ($self->isAPIOwner() && $field =~ s/^alsoinclude\s+//sio) && do { $self->alsoInclude($field); last SWITCH; }; ($callbackObj) && do { my $fallthrough = 0; my $cbName = $callbackObj->name(); print STDERR "In callback: field is '$field'\n" if ($localDebug); if ($field =~ s/^(param|field)\s+//sio) { $field =~ s/^\s+|\s+$//go; $field =~ /(\w*)\s*(.*)/so; my $paramName = $1; my $paramDesc = $2; $callbackObj->addToUserDictArray({"$paramName" => "$paramDesc"}); } elsif ($field =~ s/^internal\s+//sio) { $callbackObj->isInternal(1); } elsif ($field =~ s/^apiuid\s+//sio) { $callbackObj->requestedUID($field); } elsif ($field =~ s/^return(s)?\s+//sio) { $field =~ s/^\s+|\s+$//go; $callbackObj->addToUserDictArray({"Returns" => "$field"}); } elsif ($field =~ s/^result\s+//sio) { $field =~ s/^\s+|\s+$//go; $field =~ /(\w*)\s*(.*)/so; $callbackObj->addToUserDictArray({"Result" => "$field"}); } else { print STDERR "Adding callback field to typedef[1]. Callback name: $cbName.\n" if ($localDebug); if ($callbackObj->{ISDEFINE}) { $self->addTaggedParameter($callbackObj); } else { $self->addToFields($callbackObj); } $callbackObj = undef; # next SWITCH; $fallthrough = 1; } if (!$fallthrough) { last SWITCH; } }; ($field =~ s/^internal\s+//sio) && do { $self->isInternal(1); last SWITCH; }; ($field =~ s/^apiuid\s+//sio) && do { $self->requestedUID($field); last SWITCH; }; ($field =~ s/^serial\s+//sio) && do {$self->attribute("Serial Field Info", $field, 1); last SWITCH;}; (($class =~ /HeaderDoc\:\:Function/ || $class =~ /HeaderDoc\:\:Method/) && $field =~ s/^serialData\s+//io) && do {$self->attribute("Serial Data", $field, 1); last SWITCH;}; ($field =~ s/^serialfield\s+//sio) && do { if (!($field =~ s/(\S+)\s+(\S+)\s+//so)) { warn "$fullpath:$linenum: warning: serialfield format wrong.\n"; } else { my $name = $1; my $type = $2; my $description = "(no description)"; my $att = "$name
    \nType: $type"; $field =~ s/^(
    |\s)*//sgio; if (length($field)) { $att .= "
    \nDescription: $field"; } $self->attributelist("Serial Fields", $att, 1); } last SWITCH; }; ($field =~ s/^unformatted(\s+|$)//sio) && do { $self->preserve_spaces(1); last SWITCH; }; (!$self->isAPIOwner() && $field =~ s/^templatefield\s+//sio) && do { $self->attributelist("Template Field", $ field); last SWITCH; }; ($self->isAPIOwner() && $field =~ s/^templatefield(\s+)/$1/sio) && do { $field =~ s/^\s+|\s+$//go; $field =~ /(\w*)\s*(.*)/so; my $fName = $1; my $fDesc = $2; my $fObj = HeaderDoc::MinorAPIElement->new("LANG" => $lang, "SUBLANG" => $sublang); $fObj->apiOwner($self); $fObj->linenuminblock($linenuminblock); $fObj->blockoffset($blockoffset); # $fObj->linenum($linenum); $fObj->apiOwner($self); $fObj->outputformat($self->outputformat); $fObj->name($fName); $fObj->discussion($fDesc); $self->addToFields($fObj); # print STDERR "inserted field $fName : $fDesc"; last SWITCH; }; ($self->isAPIOwner() && $field =~ s/^super(class|)(\s+)/$2/sio) && do { $self->attribute($superclassfieldname, $field, 0); $self->explicitSuper(1); last SWITCH; }; ($self->isAPIOwner() && $field =~ s/^instancesize(\s+)/$1/sio) && do {$self->attribute("Instance Size", $field, 0); last SWITCH;}; ($field =~ s/^performance(\s+)/$1/sio) && do {$self->attribute("Performance", $field, 1); last SWITCH;}; # ($self->isAPIOwner() && $field =~ s/^subclass(\s+)/$1/sio) && do {$self->attributelist("Subclasses", $field); last SWITCH;}; ($self->isAPIOwner() && $field =~ s/^nestedclass(\s+)/$1/sio) && do {$self->attributelist("Nested Classes", $field); last SWITCH;}; ($self->isAPIOwner() && $field =~ s/^coclass(\s+)/$1/sio) && do {$self->attributelist("Co-Classes", $field); last SWITCH;}; ($self->isAPIOwner() && $field =~ s/^helper(class|)(\s+)/$2/sio) && do {$self->attributelist("Helper Classes", $field); last SWITCH;}; ($self->isAPIOwner() && $field =~ s/^helps(\s+)/$1/sio) && do {$self->attribute("Helps", $field, 0); last SWITCH;}; ($self->isAPIOwner() && $field =~ s/^classdesign(\s+)/$1/sio) && do {$self->attribute("Class Design", $field, 1); last SWITCH;}; ($self->isAPIOwner() && $field =~ s/^dependency(\s+)/$1/sio) && do {$self->attributelist("Dependencies", $field); last SWITCH;}; ($self->isAPIOwner() && $field =~ s/^ownership(\s+)/$1/sio) && do {$self->attribute("Ownership Model", $field, 1); last SWITCH;}; ($field =~ s/^security(\s+)/$1/sio) && do {$self->attribute("Security", $field, 1); last SWITCH;}; ($self->isAPIOwner() && $field =~ s/^whysubclass(\s+)/$1/sio) && do {$self->attribute("Reason to Subclass", $field, 1); last SWITCH;}; # ($self->isAPIOwner() && $field =~ s/^charset(\s+)/$1/sio) && do {$self->encoding($field); last SWITCH;}; # ($self->isAPIOwner() && $field =~ s/^encoding(\s+)/$1/sio) && do {$self->encoding($field); last SWITCH;}; (($self->isAPIOwner() || $class =~ /HeaderDoc\:\:Function/ || $class =~ /HeaderDoc\:\:Method/ || ($class =~ /HeaderDoc\:\:Var/ && $isProperty)) && $field =~ s/^(throws|exception)(\s+)/$2/sio) && do { $self->throws($field); last SWITCH; }; ($field =~ s/^namespace(\s+)/$1/sio) && do {$self->namespace($field); last SWITCH;}; ($self->isAPIOwner() && $field =~ s/^unsorted\s+//sio) && do {$self->unsorted(1); last SWITCH;}; ($field =~ s/^abstract\s+//sio) && do {$self->abstract($field); last SWITCH;}; ($field =~ s/^brief\s+//sio) && do {$self->abstract($field, 1); last SWITCH;}; ($field =~ s/^(discussion|details|description)(\s+|$)//sio) && do { # print STDERR "DISCUSSION ON $self: $field\n"; if ($class =~ /HeaderDoc\:\:PDefine/ && $self->inDefineBlock() && length($olddisc)) { # Silently drop these.... ### $self->{DISCUSSION} = ""; } if (!length($field)) { $field = "\n"; } $self->discussion($field); last SWITCH; }; ($field =~ s/^availability\s+//sio) && do {$self->availability($field); last SWITCH;}; ($field =~ s/^since\s+//sio) && do {$self->availability($field); last SWITCH;}; ($field =~ s/^author\s+//sio) && do {$self->attribute("Author", $field, 0); last SWITCH;}; ($field =~ s/^group\s+//sio) && do {$self->group($field); last SWITCH;}; ($field =~ s/^vargroup\s+//sio) && do { $field =~ s/[\n\r]/ /sg; $field =~ s/^\s*//sg; $field =~ s/\s*$//sg; $vargroup = $field; last SWITCH; }; ($class =~ /HeaderDoc\:\:PDefine/ && $self->isBlock() && $field =~ s/^hidesingletons(\s+)/$1/sio) && do {$self->{HIDESINGLETONS} = 1; last SWITCH;}; (($class =~ /HeaderDoc\:\:PDefine/ || $class =~ /HeaderDoc\:\:Function/ || $class =~ /HeaderDoc\:\:Method/) && $field =~ s/^hidecontents(\s+)/$1/sio) && do {$self->hideContents(1); last SWITCH;}; (($class =~ /HeaderDoc\:\:Function/ || $class =~ /HeaderDoc\:\:Method/) && $field =~ s/^(function|method)group\s+//sio) && do {$self->group($field); last SWITCH;}; ($class =~ /HeaderDoc\:\:PDefine/ && $field =~ s/^parseOnly(\s+|$)//sio) && do { $self->parseOnly(1); last SWITCH; }; ($class =~ /HeaderDoc\:\:PDefine/ && $field =~ s/^noParse(\s+|$)//sio) && do { print STDERR "Parsing will be skipped.\n" if ($localDebug); $HeaderDoc::skipNextPDefine = 1; last SWITCH; }; ($field =~ s/^indexgroup\s+//sio) && do {$self->indexgroup($field); last SWITCH;}; ($field =~ s/^version\s+//sio) && do {$self->attribute("Version", $field, 0); last SWITCH;}; ($field =~ s/^deprecated\s+//sio) && do {$self->attribute("Deprecated", $field, 0); last SWITCH;}; ($field =~ s/^updated\s+//sio) && do {$self->updated($field); last SWITCH;}; ($field =~ s/^attribute\s+//sio) && do { my ($attname, $attdisc, $namedisc) = &getAPINameAndDisc($field, $self->lang()); if (length($attname) && length($attdisc)) { $attdisc =~ s/^\s*//s; $attdisc =~ s/\s*$//s; $self->attribute($attname, $attdisc, 0); } else { warn "$fullpath:$linenum: warning: Missing name/discussion for attribute\n"; } last SWITCH; }; ($field =~ s/^attributelist\s+//sio) && do { $field =~ s/^\s*//so; $field =~ s/\s*$//so; my ($name, $lines) = split(/\n/, $field, 2); $name =~ s/^\s*//so; $name =~ s/\s*$//so; $lines =~ s/^\s*//so; $lines =~ s/\s*$//so; if (length($name) && length($lines)) { my @attlines = split(/\n/, $lines); foreach my $line (@attlines) { $self->attributelist($name, $line); } } else { warn "$fullpath:$linenum: warning: Missing name/discussion for attributelist\n"; } last SWITCH; }; ($field =~ s/^attributeblock\s+//sio) && do { my ($attname, $attdisc, $namedisc) = &getAPINameAndDisc($field, $self->lang()); if (length($attname) && length($attdisc)) { $self->attribute($attname, $attdisc, 1); } else { warn "$fullpath:$linenum: warning: Missing name/discussion for attributeblock\n"; } last SWITCH; }; ($field =~ /^see(also|)\s+/sio) && do { my $rest; ($field, $rest) = splitOnPara($field); $self->see($field); if ($rest =~ /\S/s) { if ($class =~ /HeaderDoc\:\:PDefine/ && $self->inDefineBlock() && length($olddisc)) { # Silently drop these.... ### $self->{DISCUSSION} = ""; } # if (!length($rest)) { $rest = "\n"; } $self->discussion($rest); }; last SWITCH; }; (($class =~ /HeaderDoc\:\:(?:Function|Method|CPPClass|ObjC(?:Container|Category|Class|Protocol))/) && $field =~ s/^var?\s+//sio) && do { $field =~ s/^\s+|\s+$//go; # $field =~ /(\w*)\s*(.*)/so; $field =~ /(\S*)\s*(.*)/so; # Let's accept any printable char for name, for pseudo enum values my $cName = $1; my $cDesc = $2; # If we can split on a newline, do it. if ($field =~ /^(.*?)\n(.*)$/so) { $cName = $1; $cDesc = $2; } my $cObj = HeaderDoc::MinorAPIElement->new("LANG" => $lang, "SUBLANG" => $sublang); $cObj->apiOwner($self); $cObj->outputformat($self->outputformat); $cObj->name($cName); $cObj->fullpath($self->fullpath()); $cObj->group($vargroup); $cObj->autodeclaration(1); $cObj->abstract($cDesc); # $cObj->discussion($cDesc); $self->addVariable($cObj); $self->apirefSetup(); my $name = $self->name(); if ($name eq "") { $name = "$cName"; $self->firstconstname($name); } last SWITCH; }; (($class =~ /HeaderDoc\:\:Enum/ || $class =~ /HeaderDoc\:\:Typedef/) && $field =~ s/^const(ant)?\s+//sio) && do { if ($self->can("isEnumList")) { $self->isEnumList(1); } $field =~ s/^\s+|\s+$//go; # $field =~ /(\w*)\s*(.*)/so; $field =~ /(\S*)\s*(.*)/so; # Let's accept any printable char for name, for pseudo enum values my $cName = $1; my $cDesc = $2; my $cObj = HeaderDoc::MinorAPIElement->new("LANG" => $lang, "SUBLANG" => $sublang); $cObj->apiOwner($self); $cObj->outputformat($self->outputformat); $cObj->name($cName); $cObj->discussion($cDesc); $self->addConstant($cObj); my $name = $self->name(); if ($name eq "") { $name = "$cName"; $self->firstconstname($name); } last SWITCH; }; (($class =~ /HeaderDoc\:\:Struct/ || $class =~ /HeaderDoc\:\:Typedef/) && $field =~ s/^callback\s+//sio) && do { $field =~ s/^\s+|\s+$//go; $field =~ /(\w*)\s*(.*)/so; my $cbName = $1; my $cbDesc = $2; if ($callbackObj && $callbackObj->{ISDEFINE}) { $self->addTaggedParameter($callbackObj); } elsif ($callbackObj) { $self->addToFields($callbackObj); } $callbackObj = HeaderDoc::MinorAPIElement->new("LANG" => $lang, "SUBLANG" => $sublang); $callbackObj->apiOwner($self); $callbackObj->outputformat($self->outputformat); $callbackObj->name($cbName); $callbackObj->discussion($cbDesc); $callbackObj->type("callback"); # $self->addToFields($callbackObj); # now get params and result that go with this callback print STDERR "Found callback. Callback name: $cbName.\n" if ($localDebug); last SWITCH; }; # param and result have to come last, since they should be handled differently, if part of a callback # which is inside a struct (as above). Otherwise, these cases below handle the simple typedef'd callback # (i.e., a typedef'd function pointer without an enclosing struct. (($class =~ /HeaderDoc\:\:Function/ || $class =~ /HeaderDoc\:\:Method/ || $class =~ /HeaderDoc\:\:Struct/ || $class =~ /HeaderDoc\:\:Typedef/ || $class =~ /HeaderDoc\:\:PDefine/ || $class =~ /HeaderDoc\:\:Var/) && $field =~ s/^(param|field)\s+//sio) && do { my $fieldname = $1; my $rest; # For better Doxygen compatibility, we should do this: # ($field, $rest) = splitOnPara($field); # but a lot of existing content assumes that @param doesn't stop # at the first whitespace, so we can't do that. $field =~ s/^\s+|\s+$//go; # trim leading and trailing whitespace # $field =~ /(\w*)\s*(.*)/so; $field =~ /(\S*)\s*(.*)/so; my $pName = $1; my $pDesc = $2; my $param = $self->taggedParamFind($pName); my $new = 0; if (!$param) { $param = HeaderDoc::MinorAPIElement->new("LANG" => $lang, "SUBLANG" => $sublang); $new = 1;} $param->apiOwner($self); $param->outputformat($self->outputformat); $param->name($pName); if ($new) { $param->discussion($pDesc); if (($class =~ /HeaderDoc\:\:Typedef/ || $class =~ /HeaderDoc\:\:Struct/) && $field =~ /^param\s+/) { $param->type("funcPtr"); $self->addToFields($param); } else { $self->addTaggedParameter($param); } } else { $param->discussion($param->discussion()."\n\n".$pDesc); } if ($class =~ /HeaderDoc\:\:Var/ && !$isProperty) { warn "Field \@$fieldname found in \@var declaration.\nYou should use \@property instead.\n" if (!$HeaderDoc::running_test); } if ($rest =~ /\S/s) { if ($class =~ /HeaderDoc\:\:PDefine/ && $self->inDefineBlock() && length($olddisc)) { # Silently drop these.... ### $self->{DISCUSSION} = ""; } # if (!length($rest)) { $rest = "\n"; } $self->discussion($rest); }; last SWITCH; }; (($class =~ /HeaderDoc\:\:Function/ || $class =~ /HeaderDoc\:\:Method/ || $class =~ /HeaderDoc\:\:Typedef/ || $class =~ /HeaderDoc\:\:Struct/ || $class =~ /HeaderDoc\:\:PDefine/ || $class =~ /HeaderDoc\:\:Var/) && $field =~ s/^return(s)?\s+//sio) && do { if ($class =~ /HeaderDoc\:\:Typedef/) { $self->isFunctionPointer(1); } if ($class =~ /HeaderDoc\:\:Var/ && !$isProperty) { warn "Field \@return found in \@var declaration.\nYou should use \@property instead.\n" if (!$HeaderDoc::running_test); } $self->result($field); last SWITCH; }; (($class =~ /HeaderDoc\:\:Function/ || $class =~ /HeaderDoc\:\:Method/ || $class =~ /HeaderDoc\:\:Typedef/ || $class =~ /HeaderDoc\:\:Struct/ || $class =~ /HeaderDoc\:\:PDefine/ || $class =~ /HeaderDoc\:\:Var/) && $field =~ s/^result\s+//sio) && do { if ($class =~ /HeaderDoc\:\:Typedef/) { $self->isFunctionPointer(1); } if ($class =~ /HeaderDoc\:\:Var/ && !$isProperty) { warn "Field \@result found in \@var declaration.\nYou should use \@property instead.\n" if (!$HeaderDoc::running_test); } $self->result($field); last SWITCH; }; ($top_level_field == 1) && do { my $keepname = 1; my $blockrequest = 0; my $isavmacro = 0; print STDERR "TLF: $field\n" if ($localDebug); my $pattern = ""; if ($class =~ /HeaderDoc\:\:PDefine/ && $field =~ s/^(availabilitymacro)(\s+|$)/$2/sio) { $self->isAvailabilityMacro(1); $keepname = 1; $self->parseOnly(1); $isavmacro = 1; } elsif ($class =~ /HeaderDoc\:\:PDefine/ && $field =~ s/^(define(?:d)?block)(\s+)/$2/sio) { $keepname = 1; $self->isBlock(1); $blockrequest = 1; } elsif ($class =~ /HeaderDoc\:\:CPPClass/) { $pattern = ":|public|private|[()]"; # print STDERR "PATTERN: $pattern\n"; } elsif ($class =~ /HeaderDoc\:\:Method/) { $pattern = ":|[()]"; } elsif ($class =~ /HeaderDoc\:\:Function/) { $pattern = "::|[()]"; } elsif ($field =~ /category\s+/) { $pattern = "[():]"; } # If we begin with the correct @whatever tag, # process it. If the tag isn't what is # expected based on what was parsed from the # code (e.g. @function for a #define), throw # away the name and tag type. if (!$blockrequest && $field =~ s/^($tag_re)(\s+|$)/$2/i) { print STDERR "tag_re[1]: tag matches\n" if ($localDebug); $keepname = 1; } elsif (!$blockrequest && !$isavmacro) { print STDERR "tag_re[2] tag does NOT match\n" if ($localDebug); # Strip off the tag! $field =~ s/(\w+)(\s|$)/$2/sio; $keepname = 0; } my ($name, $abstract_or_disc, $namedisc) = getAPINameAndDisc($field, $self->lang(), $pattern); print STDERR "FIELD: $field\nNAME: $name AOD: $abstract_or_disc ND: $namedisc" if ($localDebug); if ($fieldname eq "name") {$self->{FORCENAME} = $name; print STDERR "FORCED NAME TO \"$name\"\n"}; print STDERR "KEEPNAME: $keepname\n" if ($localDebug); if ($class =~ /HeaderDoc\:\:PDefine/ && $self->isBlock()) { print STDERR "ISBLOCK (BLOCKREQUEST=$blockrequest)\n" if ($localDebug); # my ($name, $abstract_or_disc, $namedisc) = getAPINameAndDisc($field, $self->lang()); # In this case, we get a name and abstract. if ($blockrequest) { print STDERR "Added block name $name\n" if ($localDebug); if (length($abstract_or_disc)) { if ($namedisc) { $self->name($name." ".$abstract_or_disc); # $self->nameline_discussion($abstract_or_disc); } else { $self->name($name); $self->discussion($abstract_or_disc); } } else { $self->name($name); } } else { print STDERR "Added block member $name\n" if ($localDebug); if ($callbackObj && $callbackObj->{ISDEFINE}) { $self->addTaggedParameter($callbackObj); } elsif ($callbackObj) { $self->addToFields($callbackObj); } $callbackObj = HeaderDoc::MinorAPIElement->new("LANG" => $lang, "SUBLANG" => $sublang); $callbackObj->apiOwner($self); $callbackObj->name($name); my $ref = $self->addToIncludedDefines($callbackObj); $callbackObj = ${$ref}; bless($callbackObj, "HeaderDoc::HeaderElement"); bless($callbackObj, $callbackObj->class()); if (length($abstract_or_disc)) { if ($namedisc) { $callbackObj->nameline_discussion($abstract_or_disc); } else { $callbackObj->discussion($abstract_or_disc); } } $callbackObj->{ISDEFINE} = 1; } } else { if (length($name)) { # print STDERR "NOT BLOCK. NAME IS \"$name\"\n"; if ($keepname && (!$self->isAPIOwner() || !$HeaderDoc::ignore_apiowner_names)) { print STDERR "SET NAME TO $name\n" if ($localDebug); $self->name($name); } } if (length($abstract_or_disc)) { if ($namedisc) { $self->nameline_discussion($abstract_or_disc); } else { $self->discussion($abstract_or_disc); } } } last SWITCH; }; # my $fullpath = $HeaderDoc::headerObject->fullpath(); # warn "$fullpath:$linenum: warning: Unknown field in constant comment: $field\n"; { if (length($field)) { warn "$fullpath:$linenum: warning: Unknown field (\@$field) in $tagname comment (".$self->name().")\n"; # cluck("Here\n"); } }; } $first_field = 0; if ($top_level_field) { $seen_top_level_field = 1; } } if ($callbackObj) { if ($callbackObj->{ISDEFINE}) { $self->addTaggedParameter($callbackObj); } else { $self->addToFields($callbackObj); } } # print STDERR "CLASS: ".(ref($self) || $self)." NAME: ".$self->name()." LN: ".$self->linenum()." LNIB: ".$self->linenuminblock()." BO: ".$self->blockoffset()."\n"; # $self->dbprint(); return; } # /*! # @abstract # Gets/sets whether this object's contents should be hidden. # @param self # This object. # @param hidden # The new value. (Optional.) # @discussion # Function-like macros (with curly braces) are hidden # by default. The contents of other macros can be hidden by # adding the \@hidecontents tag to the comment. # */ sub hideContents { my $self = shift; if (@_) { $self->{HIDECONTENTS} = shift; } return $self->{HIDECONTENTS}; } # /*! # @abstract # Gets/sets whether this object is a member of a define block. # @param self # This object. # @param inblock # The new value. # */ sub inDefineBlock { my $self = shift; if (@_) { $self->{INDEFINEBLOCK} = shift; } return $self->{INDEFINEBLOCK}; } # /*! # @abstract # Compares two strings in a case-insensitive fashion. # @param a # The first string. # @param b # The second string. # */ sub strcasecmp($$) { my $a = shift; my $b = shift; return (lc($a) cmp lc($b)); } # /*! # @abstract # Unregisters this object. # @param self # This object. # @discussion # This function unregisters the descendents of this # object from the UID conflict detection code and marks # this object in such a way that its UID will never be # reregistered. # # You should generally not call this unless you are about # to dispose of the object. # */ sub unregister { my $self = shift; my @arr = (); my $localDebug = 0; my $group = $self->group(); if ($group) { $self->apiOwner()->removeFromGroup($group, $self); } foreach my $tp ($self->taggedParameters()) { push(@arr, $tp); } foreach my $const ($self->constants()) { push(@arr, $const); } foreach my $variable ($self->variables()) { push(@arr, $variable); } if ($self->can("fields")) { foreach my $field ($self->fields()) { push(@arr, $field); } } foreach my $obj (@arr) { my $uid = $obj->apiuid(); print STDERR "Unregistering UID $uid\n" if ($localDebug); unregisterUID($uid, $obj->name(), $obj); unregister_force_uid_clear($uid); } $self->noRegisterUID(1); } # /*! # @abstract # Gets/sets whether this object should be banned from UID registration. # @param self # The (generally {@link //apple_ref/perl/cl/HeaderDoc::HeaderElement HeaderElement}) object. # @param value # The value to set. (Optional.) # */ sub noRegisterUID { my $self = shift; my $localDebug = 0; if (@_) { my $val = shift; print STDERR "No register uid set to $val ($self).\n" if ($localDebug); $self->{NOREGISTERUID} = $val; } return $self->{NOREGISTERUID}; } # /*! # @abstract # Wipes any cached API and/or link UIDs for this object. # @param self # This object. # */ sub wipeUIDCache { my $self = shift; my $localDebug = 0; print STDERR "APIUID and LINKUID wiped ($self).\n" if ($localDebug); $self->{APIUID} = undef; $self->{LINKUID} = undef; } # /*! # @abstract # Gets/sets whether this object's children should be suppressed. # @param self # The (generally {@link //apple_ref/perl/cl/HeaderDoc::HeaderElement HeaderElement}) object. # @param value # The value to set. (Optional.) # @discussion # An object's children are generally suppressed for the outer typedef # linked to a separate struct or enum declaration, e.g. when the # \@typedef comment is followed by an enum declaration, followed by # the actual typedef. # # When an object's children (parsed/tagged parameters, fields, etc.) # are suppressed, no UIDs are generated for the objects, and the # tagged/parsed parameter comparison does not occur. Output from any # explicitly tagged parameters is still emitted, however. The # reason for this is that the parsed parameters aren't really # associated with the typedef, so you'd get lots of warnings. # */ sub suppressChildren { my $self = shift; my $localDebug = 0; if (@_) { my $val = shift; cluck("Suppress children set to $val ($self).\n") if ($localDebug); $self->{SUPPRESSCHILDREN} = $val; } return $self->{SUPPRESSCHILDREN}; } # /*! # @abstract # Returns the "Declared in" HTML string for a class. # @param self # This class object. # */ sub declaredIn { my $self = shift; my $class = ref($self) || $self; my $apio = $self->apiOwner(); # warn $self->name()."\n"; if (!$apio) { return ""; } if ($apio->outputformat() eq "hdxml") { # blank for XML. return ""; } # print STDERR "DI: $self APIO: $apio\n"; if ($apio->{AS_FUNC_SELF}) { my $func_apio_ref = $apio->{AS_FUNC_SELF}; if (!$func_apio_ref) { die("Bad AS_FUNC_SELF for $apio (".$apio->name().")\n"); } my $func_apio = ${$func_apio_ref}; bless($func_apio, "HeaderDoc::HeaderElement"); bless($func_apio, $func_apio->class()); # print STDERR "CLASS IN FUNC\n"; my $name = $func_apio->name(); my $apiref = $func_apio->apiuid(); my $jumpTarget = $apiref; return "$name"; } if ($self->isAPIOwner()) { if ($class =~ /HeaderDoc::Header/) { # warn $self->name.": Header\n"; return ""; } else { my $name = $apio->name(); my $apiref = $apio->apiuid(); return "$name"; } } else { my $name = $apio->name(); my $apiref = $apio->apiuid(); return "$name"; } return ""; } # /*! # @abstract # Returns a custom HeaderDoc doc navigator comment for this object. # @param self # This object. # @param type # The type of the doc navigator comment. Currently valid # values are abstract, discussion # and declaration. # @param startend # The word start or end, depending # on whether you want a start or end doc navigator comment. # */ sub headerDocMark { my $self = shift; my $type = shift; my $startend = shift; my $uid = $self->apiuid(); return ""; } # /*! # @abstract # Returns a string of HTML <link> tags for # multiple external stylesheets. # @param self # This object. # @param liststring # A list of style sheets separated by spaces. # @discussion # Normally, the list comes from the HeaderDoc # config file. # */ sub doExternalStyle { my $self = shift; my $liststring = shift; my @list = split(/\s/, $liststring); my $string = ""; foreach my $styleSheet (@list) { $styleSheet =~ s/{\@docroot}/\@\@docroot/sg; $string .= "\n"; } if ($self->outputformat eq "hdxml") { return xml_fixup_links($self, $string); } else { return html_fixup_links($self, $string); } } # /*! # @abstract # Attempts to detach this object and destroy references to it to release memory. # @param self # This object. # */ sub free { my $self = shift; my $freechildrenraw = shift; # Set to 0. my $keepParseTreeAndState = shift; my $newParseTreeOwner = shift; ## if ($keepParseTreeAndState) { $self->{FAILHARD} = 1; ## printHash(%{$self}); ## print STDERR "PARSE TREES:\n"; foreach my $tree (@{$self->{PARSETREELIST}}) { print " $tree\n"; } ## print " ".$self->{PARSETREE}."\n"; ## return; } print STDERR "NPTO: $newParseTreeOwner\n" if ($HeaderDoc::debugAllocations); my $parseTree_ref = $self->parseTree(); cluck("FREEING $self\n") if ($HeaderDoc::debugAllocations); # printHash(%{$self}) if ($HeaderDoc::debugAllocations); if ($parseTree_ref) { my $parseTree = ${$parseTree_ref}; bless($parseTree, "HeaderDoc::ParseTree"); print "REMOVING FROM MAIN ".$parseTree."\n" if ($HeaderDoc::debugAllocations); # if ($self->isBlock() && $HeaderDoc::debugAllocations) { # print STDERR "Dumping parse tree for block\n"; # $parseTree->dbprint(1); # } $parseTree->apiOwnerSub($self, $newParseTreeOwner || undef, 1); $parseTree->dbprint(1) if ($HeaderDoc::debugAllocations); if (!$keepParseTreeAndState) { $parseTree->dispose(); } $self->{PARSETREE} = undef; } if ($self->{PARSETREELIST}) { foreach my $treeref (@{$self->{PARSETREELIST}}) { print "REMOVING FROM PARSETREELIST ".${$treeref}."\n" if ($HeaderDoc::debugAllocations); ${$treeref}->apiOwnerSub($self, $newParseTreeOwner || undef, 1); ${$treeref}->dbprint(1) if ($HeaderDoc::debugAllocations); if (!$keepParseTreeAndState) { ${$treeref}->dispose(); } } } $self->{PARSETREELIST} = undef; if ($self->{PARSETREECLEANUP}) { foreach my $treeref (@{$self->{PARSETREECLEANUP}}) { print "REMOVING FROM CLEANUP ".${$treeref}."\n" if ($HeaderDoc::debugAllocations); ${$treeref}->apiOwnerSub($self, $newParseTreeOwner || undef, 1); ${$treeref}->dbprint(1) if ($HeaderDoc::debugAllocations); if (!$keepParseTreeAndState) { ${$treeref}->dispose(); } } } $self->{PARSETREECLEANUP} = undef; my $state = $self->{PARSERSTATE}; if ($state && !$keepParseTreeAndState) { $state->free(); } $self->{PARSERSTATE} = undef; if (!$self->noRegisterUID()) { dereferenceUIDObject($self->apiuid(), $self); } $self->{APIOWNER} = undef; $self->{MASTERENUM} = undef; $self->{MAINOBJECT} = undef; if ($self->{NAMEREFS}) { my @arr = @{$self->{NAMEREFS}}; foreach my $name (@arr) { $HeaderDoc::namerefs{$name} = undef; } } $self->{NAMEREFS} = undef; $self->setAPIOBackReferences( $self->{CONSTANTS}, 1, $newParseTreeOwner); $self->setAPIOBackReferences( $self->{FIELDS}, 1, $newParseTreeOwner); $self->setAPIOBackReferences( $self->{VARIABLES}, 1, $newParseTreeOwner); $self->setAPIOBackReferences( $self->{INCLUDED_DEFINES}, 1, $newParseTreeOwner); $self->setAPIOBackReferences( $self->{KEEPPARAMS}, 1, $newParseTreeOwner); $self->setAPIOBackReferences( $self->{KEEPVARIABLES}, 1, $newParseTreeOwner); $self->setAPIOBackReferences( $self->{KEEPFIELDS}, 1, $newParseTreeOwner); $self->setAPIOBackReferences( $self->{KEEPCONSTANTS}, 1, $newParseTreeOwner); my $class = ref($self) || $self; if ((!$self->isBlock()) || ($newParseTreeOwner)) { $self->setAPIOBackReferences( $self->{PARSEDPARAMETERS}, 1, $newParseTreeOwner); } $self->setAPIOBackReferences( $self->{TAGGEDPARAMETERS}, 1, $newParseTreeOwner); $self->{CONSTANTS} = undef; $self->{FIELDS} = undef; $self->{VARIABLES} = undef; $self->{INCLUDED_DEFINES} = undef; $self->{ATTRIBUTELISTS} = undef; $self->{SEEALSODUPCHECK} = undef; $self->{KEEPPARAMS} = undef; $self->{KEEPVARIABLES} = undef; $self->{KEEPFIELDS} = undef; $self->{KEEPCONSTANTS} = undef; $self->{PARSEDPARAMETERS} = undef; $self->{TAGGEDPARAMETERS} = undef; if ($HeaderDoc::debugAllocations) { print STDERR "Dumping hash $self\n"; printHash(%{$self}); print STDERR "DUMPING $self WITH DEBUG INFO\n"; DumpWithOP($self); # Dump($self); } $self = (); } # /*! # @abstract # Helper called by Perl when this object gets freed. # @param self # This object. # */ sub DESTROY { my $self = shift; print STDERR "Destroying $self\n" if ($HeaderDoc::debugAllocations); } # /*! # @abstract # Prints a variable (e.g. an array) in detail for debugging purposes. # @param unknown_var # The variable to print. # @param leadspace # Leading space characters (or other text) to add to # each line of output. # @discussion # Called by {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/dbprint//() dbprint}. # Do not call directly. # */ sub dbprint_expanded { my $unknown_var = shift; my $leadspace = ""; if ($_) { $leadspace = shift; } if ($unknown_var =~ "REF") { $unknown_var = ref($unknown_var); } my $retstring = $unknown_var; if (ref($unknown_var) eq "ARRAY") { print STDERR "REF IS ".ref($unknown_var)."\n"; $retstring .= "\n".$leadspace."ARRAY ELEMENTS:\n"; my $pos = 0; while ($pos < scalar(@{$unknown_var})) { $retstring .= $leadspace." ".sprintf("%08d : ", $pos). dbprint_expanded(@{$unknown_var}[$pos], $leadspace." ")."\n"; $pos++; } } elsif (ref($unknown_var) ne "") { $retstring .= "\n".$leadspace."HASH ELEMENTS:\n"; # print STDERR "REF IS ".ref($unknown_var)."\n"; foreach my $elt (keys %{$unknown_var}) { if ($elt =~ "APIOWNER" || $elt =~ "MASTERENUM") { next; } $retstring .= $leadspace." ".sprintf("%8s : ", $elt). dbprint_expanded($unknown_var->{$elt}, $leadspace." ")."\n"; } } return $retstring;; } # /*! # @abstract # Gets/sets whether this object is a block declaration. # @param self # This object. # @param value # The value to set. (Optional.) # @discussion # This should be set to a nonzero value for define blocks # and blocks of functions wrapped with #if blocks. # */ sub isBlock { my $self = shift; if (@_) { $self->{ISBLOCK} = shift; if ($self->{ISBLOCK}) { $self->fixParseTrees(); } } return $self->{ISBLOCK}; } # /*! # @abstract # Prints this object for debugging purposes. # @param self # This object. # @param expanded # Pass 1 to expand arrays and other structure, # or 0 for normal output. # */ sub dbprint { my $self = shift; my $expanded = shift; my @keys = keys %{$self}; print STDERR "Dumping object $self...\n"; foreach my $key (@keys) { if ($expanded) { print STDERR "$key => ".dbprint_expanded($self->{$key})."\n"; } else { print STDERR "$key => ".$self->{$key}."\n"; } } print STDERR "End dump of object $self.\n"; } # /*! # @abstract # Gets/sets whether spaces in the declaration should be preserved. # @param self # This object. # @param value # The value to set. (Optional.) # @discussion # This is set high by the presence of an \@unformatted tag in # the HeaderDoc markup. # */ sub preserve_spaces { my $self = shift; if (@_) { $self->{PRESERVESPACES} = shift; } if (!defined($self->{PRESERVESPACES})) { return 0; } return $self->{PRESERVESPACES}; } #/*! # @abstract # Returns whether this is a framework object. # @discussion # This is overridden in {@link HeaderDoc::CPPClass}. # */ sub isFramework { return 0; } #/*! # @abstract # Returns this object's tag type for use in Doxygen-style tag files. # @param self # This object. # */ sub getDoxyKind { my $self = shift; my $class = ref($self) || $self; if ($self->isAPIOwner()) { if ($class =~ /HeaderDoc::Header/) { return "file"; } return "class"; } if ($class =~ /HeaderDoc::Function/ || $class =~ /HeaderDoc::Method/) { return "function"; } if ($class =~ /HeaderDoc::Constant/) { return "variable"; } if ($class =~ /HeaderDoc::Var/) { return "variable"; } if ($class =~ /HeaderDoc::Struct/) { return "struct"; } if ($class =~ /HeaderDoc::Typedef/) { return "typedef"; } if ($class =~ /HeaderDoc::Union/) { return "union"; } if ($class =~ /HeaderDoc::Enum/) { return "enumeration"; } if ($class =~ /HeaderDoc::PDefine/) { return "define"; } if ($class =~ /HeaderDoc::MinorAPIElement/) { my $parent = $self->apiOwner(); my $parentclass = ref($parent) || $parent; if ($parentclass =~ /HeaderDoc::Enum/) { return "enumvalue"; } else { return "variable"; } } } # /*! # @abstract # Gets a Doxygen-style tag string for this object. # @param self # This object. # @param prespace # Leading whitespace to prepend to each line. # */ sub _getDoxyTagString { my $self = shift; my $prespace = shift; my $class = ref($self) || $self; my $doxyTagString = ""; my $kind = $self->getDoxyKind(); my $accessControl = ""; if ($self->can("accessControl")) { $accessControl = $self->accessControl(); } if ($accessControl =~ /\S/) { $accessControl = " protection=\"$accessControl\""; } else { $accessControl = ""; } $doxyTagString .= "$prespace\n"; my $arglist = ""; my $type = ""; if ($class =~ /HeaderDoc::Function/ || $class =~ /HeaderDoc::Method/) { $type = $self->returntype(); $type =~ s/^\s*//s; $type =~ s/\s*$//s; $type =~ s/\s*\.\s*/./s; $doxyTagString .= "$prespace $type\n"; my @args = $self->parsedParameters(); my $comma = ""; foreach my $obj (@args) { $arglist .= $comma; my $type .= $obj->type(); if ($class =~ /HeaderDoc::Method/) { my $tagname = $obj->tagname(); if ($tagname =~ /\S/) { $arglist .= "[$tagname] "; } $type =~ s/^\s*\(//s; $type =~ s/\)\s*$//s; } $arglist .= $self->textToXML($type); if ($arglist !~ /[ *]$/) { $arglist .= " "; } $arglist .= $obj->name(); $comma = ","; } $arglist = "($arglist)"; } elsif ($class =~ /HeaderDoc::Var/ || $class =~ /HeaderDoc::Constant/) { $type = $self->returntype(); $type =~ s/^\s*//s; $type =~ s/\s*$//s; $type =~ s/\s*\.\s*/./s; $doxyTagString .= "$prespace ".$self->textToXML($type)."\n"; } $doxyTagString .= "$prespace ".$self->textToXML($self->rawname())."\n"; # No handling of superclass here because that's handled in APIOwner.pm. $doxyTagString .= "$prespace \n"; $doxyTagString .= "$prespace \n"; $doxyTagString .= "$prespace $arglist\n"; $doxyTagString .= "$prespace\n"; return $doxyTagString; } # /*! # @abstract # Sets an override API uid for the rare cases where they would # conflict by nature. # @discussion # This is triggered by the presence of an \@apiuid tag in the # HeaderDoc comment block (usually for a header file). # */ sub requestedUID { my $self = shift; if (@_) { my $rquid = shift; $rquid =~ s/^\s*//s; $rquid =~ s/\s*$//sg; # print "RQUID: \"$rquid\"\n"; if ($rquid =~ /\s/) { warn($self->filename().":".$self->linenum().": WARNING: Ignoring illegal apple_ref markup (contains whitespace)\n"); return $self->{REQUESTEDUID}; } # Ideally, this should probably do something like this: # if ($self->{REQUESTEDUID}) { # unregisterUID($self->{REQUESTEDUID}, $self->name(), $self); # }; # if ($self->{APIUID}) { # $unregisterUID($self->{APIUID}, $self->name(), $self); # }; $self->{REQUESTEDUID} = $rquid; registerUID($rquid, $self->rawname(), $self); } return $self->{REQUESTEDUID}; } # /*! # @abstract # Prints a dump of function and #define # declarations in this header for debugging purposes. # @param self # This object. # */ sub headerDump { my $self = shift; print "HEADER\n"; print " |\n"; print " +-- Functions\n"; foreach my $obj ($self->functions()) { print " | +-- ".$obj->name()."\n"; print " | | +-- obj: ".$obj."\n"; print " | | +-- isBlock: ".$obj->isBlock()."\n"; } print " |\n"; print " +-- #defines\n"; foreach my $obj ($self->pDefines()) { print " | +-- ".$obj->name()."\n"; print " | | +-- obj: ".$obj."\n"; print " | | +-- isBlock: ".$obj->isBlock()."\n"; } } # /*! # @abstract # Returns the method type portion of the API reference for a # C++ method. # @param self # This HeaderElement object. # */ sub getMethodType { my $self = shift; my $class = ref($self) || $self; my $apio = $self->apiOwner(); my $apioclass = ref($apio) || $apio; # This probably won't ever really be encountered, but.... if ($class =~ /HeaderDoc::PDefine/) { return "define"; } if ($apioclass eq "HeaderDoc::Header") { return "func"; } my $typename = "instm"; if ($class =~ /HeaderDoc::Method/) { # Objective-C if (!$self->parserState()) { cluck("We shouldn't hit this ($self).\n"); } if ($self->parserState()->{occmethodtype} eq "+") { $self->setIsInstanceMethod("NO"); if ($apioclass =~ /HeaderDoc::ObjCProtocol/) { $typename = "intfcm"; } else { $typename = "clm"; } } else { $self->setIsInstanceMethod("YES"); if ($apioclass =~ /HeaderDoc::ObjCProtocol/) { $typename = "intfm"; } else { $typename = "instm"; } } } else { if (($self->lang() ne "C") && ($self->lang() ne "Csource")) { return "instm"; } if ($self->sublang() eq "C") { # COM interfaces, C pseudoclasses return "intfm"; } if ($self->returntype() =~ /(^|\s)static(\s|$)/) { $typename = "clm"; } else { $typename = "instm"; } } if ($self->isTemplate()) { $typename = "ftmplt"; } return $typename; } # /*! # @abstract # Gets/sets the parser state. # @param self # This object. # @param newstate # The new value. (Optional.) # @discussion # Stores a clone of the parser state object for future # reference. Rarely used, but important for Objective-C # method type identification. # */ sub parserState { my $self = shift; if (@_) { my $newstate = shift; # print STDERR "OBJ $self STATE $newstate\n"; $self->{PARSERSTATE} = $newstate; } return $self->{PARSERSTATE}; } # /*! # @abstract # Gets/sets the ISINTERNAL flag. # */ sub isInternal { my $self = shift; if (@_) { my $newval = shift; $self->{ISINTERNAL} = $newval; } return $self->{ISINTERNAL}; } # /*! @abstract # Sets and returns the namespace of this class. # @param self # The {@link //apple_ref/perl/cl/HeaderDoc::APIOwner APIOwner} object. # @param namespace # The namespace value to set. (Optional) # */ sub namespace { my $self = shift; my $localDebug = 0; if (@_) { $self->{NAMESPACE} = shift; } print STDERR "namespace ".$self->{NAMESPACE}."\n" if ($localDebug); return $self->{NAMESPACE}; } # /*! # @abstract # Clears references to higher nodes in the object graph. # @param self # The {@link //apple_ref/perl/cl/HeaderDoc::APIOwner APIOwner} # object. (Unused) # @param listref # A list of objects to modify. # @param freechildren # Pass 1 to call the # {@link //apple_ref/perl/instm/HeaderDoc::HeaderElement/free//() free} # method on this object's children, else 0. # */ sub setAPIOBackReferences { my $self = shift; my $listref = shift; my $freechildren = shift; my $newowner = shift; if ($listref) { my @list = @{$listref}; foreach my $item (@list) { $item->{APIOWNER} = $newowner; if ($freechildren && !$newowner) { $item->free(); } } } } # /*! # @abstract # Adds a name to the {@link NAMEREFS} array. # @discussion # In {@link //apple_ref/doc/header/BlockParse.pm BlockParse.pm}, # HeaderDoc adds each object into the associative array # {@link HeaderDoc::namerefs}. # Because Perl's garbage collection is a joke, this reference must # be destroyed to free the object. # */ sub addToNameRefs { my $self = shift; my $name = shift; my @arr = (); if ($self->{NAMEREFS}) { @arr = @{$self->{NAMEREFS}}; } push(@arr, $name); $self->{NAMEREFS} = \@arr; } # /*! # @abstract # Adds a name to the {@link PARSETREECLEANUP} array. # @discussion # In {@link //apple_ref/doc/header/BlockParse.pm BlockParse.pm}, # HeaderDoc sets the owner of a parse tree to an API object. # If that object goes away, that association needs to be # cleared. This array keeps track of that. # */ sub addToCleanup { my $self = shift; my $name = shift; my @arr = (); if ($self->{PARSETREECLEANUP}) { @arr = @{$self->{PARSETREECLEANUP}}; } push(@arr, $name); $self->{PARSETREECLEANUP} = \@arr; } # /*! # @abstract # Returns the character set encoding of the enclosing # file. # */ sub encoding { my $self = shift; return $self->apiOwner()->encoding(); } # /*! # @abstract # Gets/sets the result (return values) text for functions, # Objective-C methods, function-like macros, structures # containing callbacks, type definitions containing callbacks, # and Objective-C properties. # etc. # @param self # The Function object. # @param resultstring # The string to set. (Optional.) # @discussion # This comes from the \@result, \@return, or # \@returns tag in the HeaderDoc comment. # */ sub result { my $self = shift; if (@_) { $self->{RESULT} = shift; } return filterHeaderDocTagContents($self->{RESULT}); } # /*! # @abstract # Gets/sets type conversion history. # @discussion # In {@link blockParseOutside}, when you request a # specific type (e.g \@function), an object of that # type is generated. If the resulting declaration # should be allowed to match against that type, a # conversion occurs, in which a new object of a # different type is created with the same contents. # # When a conversion occurs, this function records the # original requested type for later use. # @param self # The HeaderElement object. This is # actually applied to the original (pre-conversion) # HeaderElement object, but since the # underlying keys are copied to the new object, # it "just works". # @param origtype # The new value to set. (Optional.) # */ sub origType { my $self = shift; if (@_) { my $origtype = shift; $self->{ORIGTYPE} = $origtype; # print STDERR "Set original type to $origtype\n"; } return $self->{ORIGTYPE}; } # /*! # @abstract # Maintains the list of auto-related references used for # mixed group handling. # @discussion # When you have a mixed block containing functions and # defines, the INCLUDED_DEFINES array ceases to be a # workable way to find out what is in that block. This # function stores and retrieves an array containing # only the API references (UIDs) for machine-generated # "See Also" cross-references. # */ sub autoRelate { my $self = shift; my @ar = (); if ($self->{AUTORELATE}) { @ar = @{$self->{AUTORELATE}}; } if (@_) { my $newvalue = shift; my @parts = split(/\s+/, $newvalue); push(@ar, $parts[1]); } $self->{AUTORELATE} = \@ar; return $self->{AUTORELATE}; } # /*! # @abstract # Stores a backup copy of the abstract and discussion # for later use or checks to see if one has been stored. # @param self # The HeaderElement object to modify. # @param set # If a second parameter is specified, a backup is made. # @result # Returns whether the discussion has been backed up or not. # @discussion # Used when parsing \@defineblock blocks to restore # the discussion and abstract of the block. # */ sub discussionLocked { my $self = shift; if (@_) { $self->{DISCUSSIONLOCKED} = $self->{DISCUSSION}; $self->{ABSTRACTLOCKED} = $self->{ABSTRACT}; $self->{DISCUSSION_SETLOCKED} = $self->{DISCUSSION_SET}; } return $self->{DISCUSSIONLOCKED} ? 1 : 0; } # /*! # @abstract # Restores a backup copy of the abstract and discussion # stored by {@link discussionLocked}. # @param self # The HeaderElement object to modify. # @discussion # Used when parsing \@defineblock blocks to restore # the discussion and abstract of the block. # */ sub unlockDiscussion { my $self = shift; $self->{DISCUSSION} = $self->{DISCUSSIONLOCKED}; $self->{ABSTRACT} = $self->{ABSTRACTLOCKED}; $self->{DISCUSSION_SET} = $self->{DISCUSSION_SETLOCKED}; } # /*! # @abstract # Wipes the discussion and abstract so that # defines in a \@defineblock can be # correctly parsed. # @param self # The HeaderElement object to modify. # */ sub prepareDiscussionForTemporary { my $self = shift; $self->{DISCUSSION} = undef; $self->{ABSTRACT} = undef; $self->{DISCUSSION_SET} = undef; } # /*! # @abstract # Clones a function object for use as an API owner for any # enclosing scripts. # */ sub cloneAppleScriptFunctionContents { my $self = shift; my $localDebug = 0; if ($localDebug) { print STDERR "Cloning $self for AppleScript function body parsing.\n"; $self->dbprint(); } my $class_self = HeaderDoc::CPPClass->new(); $self->{AS_CLASS_SELF} = \$class_self; $class_self->{AS_FUNC_SELF} = \$self; my $orig_ptref = $self->{PARSETREE}; bless($orig_ptref, "HeaderDoc::ParseTree"); my $parseTree = ${$orig_ptref}; $parseTree = $parseTree->ASFunctionBodyStart(); my $tree = $parseTree->clone(); $tree->parserState($self->{PARSERSTATE}); # Don't copy the name here, because it hasn't been set yet, but copy # the parse tree, because otherwise it gets stomped. $class_self->{PARSETREE} = \$tree; $class_self->{PARSERSTATE} = $self->{PARSERSTATE}; $class_self->{SUBLANG} = $self->{SUBLANG}; $class_self->{APIOWNER} = $self->{APIOWNER}; $class_self->{FILENAME} = $self->{FILENAME}; $class_self->{FULLPATH} = $self->{FULLPATH}; $class_self->{OUTPUTFORMAT} = $self->{OUTPUTFORMAT}; print STDERR "NAME: ".$class_self->name()."\n" if ($localDebug); print STDERR "RAWNAME: ".$class_self->rawname()."\n" if ($localDebug); print STDERR "TREE: $tree\n" if ($localDebug); $tree->apiOwner($class_self); } # /*! # @abstract # Processes the cloned function/class object previously # created by the {@link cloneAppleScriptFunctionContents} # method. # */ sub processAppleScriptFunctionContents { my $self = shift; my $localDebug = 0; print STDERR "IN processAppleScriptFunctionContents\n" if ($localDebug); $self->dbprint() if ($localDebug); # Grab the class object that contains the parsed contents of the function body. my $class_self_ref = $self->{AS_CLASS_SELF}; if (!$class_self_ref) { die("Missing AS_CLASS_SELF for $self (".$self->name().")\n"); } my $class_self = ${$class_self_ref}; bless($class_self, "HeaderDoc::HeaderElement"); bless($class_self, $class_self->class()); # Copy the name and other info, now that it is known. $class_self->{NAME} = $self->{NAME}; $class_self->{RAWNAME} = $self->{RAWNAME}; $class_self->{FORCENAME} = $self->{FORCENAME}; $class_self->{NAMEREFS} = $self->{NAMEREFS}; $class_self->{LANG} = $self->{LANG}; $class_self->{OUTPUTFORMAT} = $self->{OUTPUTFORMAT}; # Determine the output mode. my $xml_output = 0; my $apiOwner = $self->apiOwner(); if ($apiOwner->outputformat() eq "hdxml") { $xml_output = 1; } my $apioclass = ref($apiOwner) || $apiOwner; if ($apioclass =~ /HeaderDoc::Header/) { $class_self->{HEADEROBJECT} = $apiOwner; } else { $class_self->{HEADEROBJECT} = $apiOwner->{HEADEROBJECT}; } print STDERR "OF: ".$apiOwner->outputformat()."\n" if ($localDebug); # Obtain the parse tree. my $ptref = $class_self->{PARSETREE}; bless($ptref, "HeaderDoc::ParseTree"); my $parseTree = ${$ptref}; # Process the embedded tags and write out the contents. $parseTree->processEmbeddedTags($xml_output, $class_self); # print STDERR "PROCESSED ".$class_self->name()."\n"; my @has_classes = $class_self->classes(); # print STDERR "WRITING ".scalar(@has_classes)." CLASSES\n"; if (!scalar(@has_classes)) { return undef; } # Compute the name of the directory where the contents should be written (in HTML mode) if (!$xml_output) { my $className = $class_self->name(); # for now, always shorten long names since some files may be moved to a Mac for browsing if (1 || $isMacOS) {$className = &safeName(filename => $className);}; $className = "parsedFunctionContents_$className"; # my $classesDir = $self->apiOwner()->classesDir(); # if (!$HeaderDoc::running_test) { # if (! -e $classesDir) { # unless (mkdir ("$classesDir", 0777)) {die ("Can't create output folder $classesDir. \n$!");}; # } # } my $classRootDir = $self->apiOwner()->{OUTPUTDIR}; $class_self->outputDir("$classRootDir$pathSeparator$className"); $class_self->{PARSEDPSEUDOCLASSNAME} = $className; } # Write the output (in HTML mode) if (!$xml_output) { if ($class_self->classes()) { print STDERR "CLASSES\n" if ($localDebug); if (!$HeaderDoc::running_test) { $class_self->writeHeaderElements(); } } else { print STDERR "NO CLASSES\n" if ($localDebug); } } $self->{ASCONTENTSPROCESSED} = 1; $parseTree->dbprint() if ($localDebug); return $class_self; } 1;