.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "TreeDumper 3pm" .TH TreeDumper 3pm "2023-11-02" "perl v5.36.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Data::TreeDumper \- Improved replacement for Data::Dumper. Powerful filtering capability. .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Data::TreeDumper ; \& \& my $sub = sub {} ; \& \& my $s = \& { \& A => \& { \& a => \& { \& } \& , bbbbbb => $sub \& , c123 => $sub \& , d => \e$sub \& } \& \& , C => \& { \& b => \& { \& a => \& { \& a => \& { \& } \& \& , b => sub \& { \& } \& , c => 42 \& } \& \& } \& } \& , ARRAY => [qw(element_1 element_2 element_3)] \& } ; \& \& \& #\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& # package setup data \& #\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& \& $Data::TreeDumper::Useascii = 0 ; \& $Data::TreeDumper::Maxdepth = 2 ; \& \& print DumpTree($s, \*(Aqtitle\*(Aq) ; \& print DumpTree($s, \*(Aqtitle\*(Aq, MAX_DEPTH => 1) ; \& print DumpTrees \& ( \& [$s, "title", MAX_DEPTH => 1] \& , [$s2, "other_title", DISPLAY_ADDRESS => 0] \& , USE_ASCII => 1 \& , MAX_DEPTH => 5 \& ) ; .Ve .SH "Output" .IX Header "Output" .Vb 10 \& title: \& |\- A [H1] \& | |\- a [H2] \& | |\- bbbbbb = CODE(0x8139fa0) [C3] \& | |\- c123 [C4 \-> C3] \& | \`\- d [R5] \& | \`\- REF(0x8139fb8) [R5 \-> C3] \& |\- ARRAY [A6] \& | |\- 0 [S7] = elment_1 \& | |\- 1 [S8] = element_2 \& | \`\- 2 [S9] = element_3 \& \`\- C [H10] \& \`\- b [H11] \& \`\- a [H12] \& |\- a [H13] \& |\- b = CODE(0x81ab130) [C14] \& \`\- c [S15] = 42 .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" Data::Dumper and other modules do a great job of dumping data structures. Their output, however, often takes more brain power to understand than the data itself. When dumping large amounts of data, the output can be overwhelming and it can be difficult to see the relationship between each piece of the dumped data. .PP Data::TreeDumper also dumps data in a tree-like fashion but \fIhopefully\fR in a format more easily understood. .SS "Label" .IX Subsection "Label" Each node in the tree has a label. The label contains a type and an address. The label is displayed to the right of the entry name within square brackets. .PP .Vb 4 \& | |\- bbbbbb = CODE(0x8139fa0) [C3] \& | |\- c123 [C4 \-> C3] \& | \`\- d [R5] \& | \`\- REF(0x8139fb8) [R5 \-> C3] .Ve .PP \fIAddress\fR .IX Subsection "Address" .PP The addresses are linearly incremented which should make it easier to locate data. If the entry is a reference to data already displayed, a \fB\-\fR> followed with the address of the already displayed data is appended within the label. .PP .Vb 5 \& ex: c123 [C4 \-> C3] \& ^ ^ \& | | address of the data referred to \& | \& | current element address .Ve .PP \fITypes\fR .IX Subsection "Types" .PP \&\fBS\fR: Scalar, \&\fBH\fR: Hash, \&\fBA\fR: Array, \&\fBC\fR: Code, .PP \&\fBR\fR: Reference, \&\fB\s-1RS\s0\fR: Scalar reference. \&\fBOx\fR: Object, where x is the object underlying type .SS "Empty Hash or Array" .IX Subsection "Empty Hash or Array" No structure is displayed for empty hashes or arrays, the string \*(L"no elements\*(R" is added to the display. .PP .Vb 3 \& |\- A [S10] = string \& |\- EMPTY_ARRAY (no elements) [A11] \& |\- B [S12] = 123 .Ve .SH "Configuration and Overrides" .IX Header "Configuration and Overrides" Data::TreeDumper has configuration options you can set to modify the output it generates. \fIDumpTree\fR and \fIPrintTree\fR take overrides as trailing arguments. Those overrides are active within the current dump call only. .PP .Vb 2 \& ex: \& $Data::TreeDumper::Maxdepth = 2 ; \& \& # maximum depth set to 1 for the duration of the call only \& print DumpTree($s, \*(Aqtitle\*(Aq, MAX_DEPTH => 1) ; \& PrintTree($s, \*(Aqtitle\*(Aq, MAX_DEPTH => 1) ; # shortcut for the above call \& \& # maximum depth is 2 \& print DumpTree($s, \*(Aqtitle\*(Aq) ; .Ve .ie n .SS "$Data::TreeDumper::Displaycallerlocation" .el .SS "\f(CW$Data::TreeDumper::Displaycallerlocation\fP" .IX Subsection "$Data::TreeDumper::Displaycallerlocation" This package variable is very useful when you use \fBData::TreeDumper\fR and don't know where you called \&\fBPrintTree\fR or \fBDumpTree\fR, ie when debugging. It displays the filename and line of call on \s-1STDOUT.\s0 It can also be set as an override, \s-1DISPLAY_CALLER_LOCATION\s0 => 1. .SS "\s-1NO_PACKAGE_SETUP\s0" .IX Subsection "NO_PACKAGE_SETUP" Sometimes, the package setup you have is not what you want to use. resetting the variable, making a call and setting the variables back is boring. You can set \fB\s-1NO_PACKAGE_SETUP\s0\fR to 1 and \fIDumpTree\fR will ignore the package setup for the call. .PP .Vb 2 \& print Data::TreeDumper::DumpTree($s, "Using package data") ; \& print Data::TreeDumper::DumpTree($s, "Not Using package data", NO_PACKAGE_SETUP => 1) ; .Ve .SS "\s-1DISPLAY_ROOT_ADDRESS\s0" .IX Subsection "DISPLAY_ROOT_ADDRESS" By default, \fBData::TreeDumper\fR doesn't display the address of the root. .PP .Vb 1 \& DISPLAY_ROOT_ADDRESS => 1 # show the root address .Ve .SS "\s-1DISPLAY_ADDRESS\s0" .IX Subsection "DISPLAY_ADDRESS" When the dumped data is not self-referential, displaying the address of each node clutters the display. You can direct \fBData::TreeDumper\fR to not display the node address by using: .PP .Vb 1 \& DISPLAY_ADDRESS => 0 .Ve .SS "\s-1DISPLAY_PATH\s0" .IX Subsection "DISPLAY_PATH" Add the path of the element to its address. .PP .Vb 1 \& DISPLAY_PATH => 1 \& \& ex: \*(Aq\- CopyOfARRAY [A39 \-> A18 /{\*(AqARRAY\*(Aq}] .Ve .SS "\s-1DISPLAY_OBJECT_TYPE\s0" .IX Subsection "DISPLAY_OBJECT_TYPE" \&\fBData::TreeDumper\fR displays the package in which an object is blessed. You can suppress this display by using: .PP .Vb 1 \& DISPLAY_OBJECT_TYPE => 0 .Ve .SS "\s-1DISPLAY_INHERITANCE\s0" .IX Subsection "DISPLAY_INHERITANCE" \&\fBData::TreeDumper\fR will display the inheritance hierarchy for the object: .PP .Vb 2 \& |\- object = blessed in \*(AqSuperObject\*(Aq <\- Potatoe [OH55] \& | \`\- Data = 0 [S56] .Ve .SS "\s-1DISPLAY_AUTOLOAD\s0" .IX Subsection "DISPLAY_AUTOLOAD" if set, \fBData::TreeDumper\fR will tag the object type with '[A]' if the package has an \s-1AUTOLOAD\s0 function. .PP .Vb 2 \& |\- object_with_autoload = blessed in \*(Aq[A]SuperObjectWithAutoload\*(Aq <\- Potatoe <\- [A] Vegetable [O58] \& | \`\- Data = 0 [S56] .Ve .SS "\s-1DISPLAY_TIE\s0" .IX Subsection "DISPLAY_TIE" if \s-1DISPLAY_TIE\s0 is set, \fBData::TreeDumper\fR will display which package the variable is tied to. This works for hashes and arrays as well as for objects which are based on hashes and arrays. .PP .Vb 2 \& |\- tied_hash (tied to \*(AqTiedHash\*(Aq) [H57] \& | \`\- x = 1 [S58] \& \& |\- tied_hash_object = (tied to \*(AqTiedHash\*(Aq) blessed in \*(AqSuperObject\*(Aq <\- [A]Potatoe <\- Vegetable [O59] \& | |\- m1 = 1 [S60] \& | \`\- m2 = 2 [S61] .Ve .SS "\s-1PERL DATA\s0" .IX Subsection "PERL DATA" Setting one of the options below will show internal perl data: .PP .Vb 7 \& Cells: <2234> HASH(0x814F20c) \& |\- A1 [H1] <204> HASH(0x824620c) \& | \`\- VALUE [S2] = datadatadatadatadatadatadatadatadatadata <85> \& |\- A8 [H11] <165> HASH(0x8243d68) \& | \`\- VALUE [S12] = C <46> \& \`\- C2 [H19] <165> HASH(0x8243dc0) \& \`\- VALUE [S20] = B <46> .Ve .PP \fI\s-1DISPLAY_PERL_SIZE\s0\fR .IX Subsection "DISPLAY_PERL_SIZE" .PP Setting this option will show the size of the memory allocated for each element in the tree within angle brackets. .PP .Vb 1 \& DISPLAY_PERL_SIZE => 1 .Ve .PP The excellent Devel::Size is used to compute the size of the perl data. If you have deep circular data structures, expect the dump time to be slower, 50 times slower or more. .PP \fI\s-1DISPLAY_PERL_ADDRESS\s0\fR .IX Subsection "DISPLAY_PERL_ADDRESS" .PP Setting this option will show the perl-address of the dumped data. .PP .Vb 1 \& DISPLAY_PERL_ADDRESS => 1 .Ve .SS "\s-1REPLACEMENT_LIST\s0" .IX Subsection "REPLACEMENT_LIST" Scalars may contain non-printable characters that you would rather not see in a dump. One of the most common is \*(L"\er\*(R" embedded in a text string from MS-DOS files. \fBData::TreeDumper\fR, by default, replaces \*(L"\en\*(R" by \&'[\en]' and \*(L"\er\*(R" by '[\er]'. You can set \s-1REPLACEMENT_LIST\s0 to an array ref containing elements which are themselves array references. The first element is the character(s) to match and the second is the replacement. .PP .Vb 3 \& # a fancy and stricter replacement for \en and \er \& my $replacement = [ ["\en" => \*(Aq[**Fancy \en replacement**]\*(Aq], ["\er" => \*(Aq\er\*(Aq] ] ; \& print DumpTree($smed\->{TEXT}, \*(AqText:\*(Aq, REPLACEMENT_LIST => $replacement) ; .Ve .SS "\s-1QUOTE_HASH_KEYS\s0" .IX Subsection "QUOTE_HASH_KEYS" \&\fB\s-1QUOTE_HASH_KEYS\s0\fR and its package variable \fB\f(CB$Data::TreeDumper::Quotehashkeys\fB\fR can be set if you wish to single-quote the hash keys. Hash keys are not quoted by default. .PP .Vb 1 \& DumpTree(\e$s, \*(Aqsome data:\*(Aq, QUOTE_HASH_KEYS => 1) ; \& \& # output \& some data: \& \`\- REF(0x813da3c) [H1] \& |\- \*(AqA\*(Aq [H2] \& | |\- \*(Aqa\*(Aq [H3] \& | |\- \*(Aqb\*(Aq [H4] \& | | |\- \*(Aqa\*(Aq = 0 [S5] .Ve .SS "\s-1DISPLAY_NO_VALUE\s0" .IX Subsection "DISPLAY_NO_VALUE" Only element names are added to the tree rendering .SS "\s-1QUOTE_VALUES\s0" .IX Subsection "QUOTE_VALUES" \&\fB\s-1QUOTE_VALUES\s0\fR and its package variable \fB\f(CB$Data::TreeDumper::Quotevalues\fB\fR can be set if you wish to single-quote the scalar values. .PP .Vb 1 \& DumpTree(\e$s, \*(AqCells:\*(Aq, QUOTE_VALUES=> 1) ; .Ve .SS "\s-1NO_NO_ELEMENTS\s0" .IX Subsection "NO_NO_ELEMENTS" If this option is set, \fBData::TreeDumper\fR will not add 'no elements' to empty hashes and arrays .SS "\s-1NO_OUTPUT\s0" .IX Subsection "NO_OUTPUT" This option suppresses all output generated by Data::TreeDumper. This is useful when you want to iterate through your data structures and display the data yourself, manipulate the data structure, or do a search (see \*(L"using filter as iterators\*(R" below) .SS "Filters" .IX Subsection "Filters" Data::TreeDumper can sort the tree nodes with a user-defined subroutine. By default, hash keys are sorted. .PP .Vb 2 \& FILTER => \e&ReverseSort \& FILTER_ARGUMENT => [\*(Aqyour\*(Aq, \*(Aqarguments\*(Aq] .Ve .PP The filter routine is passed these arguments: .IP "1 \- a reference to the node which is going to be displayed" 2 .IX Item "1 - a reference to the node which is going to be displayed" .PD 0 .IP "2 \- the node's depth (this allows you to selectively display elements at a certain depth)" 2 .IX Item "2 - the node's depth (this allows you to selectively display elements at a certain depth)" .IP "3 \- the path to the reference from the start of the dump." 2 .IX Item "3 - the path to the reference from the start of the dump." .ie n .IP "4 \- an array reference containing the keys to be displayed (see ""Filter chaining"")" 2 .el .IP "4 \- an array reference containing the keys to be displayed (see ``Filter chaining'')" 2 .IX Item "4 - an array reference containing the keys to be displayed (see Filter chaining)" .IP "5 \- the dumper's setup" 2 .IX Item "5 - the dumper's setup" .IP "5 \- the filter arguments (see below)" 2 .IX Item "5 - the filter arguments (see below)" .PD .PP The filter returns the node's type, possibly a new structure (see below) and a list of 'keys' to display. The keys are hash keys or array indexes. .PP In Perl: .PP .Vb 1 \& ($tree_type, $replacement_tree, @nodes_to_display) = $your_filter\->($tree, $level, $path, $nodes_to_display, $setup) ; .Ve .PP Filters are not as complicated as they sound and they are very powerful, especially when using the path argument. The path idea was given to me by another module writer but I forgot whom. If this writer will contact me, I will give him the proper credit. .PP Lots of examples can be found in \fIfilters.pl\fR and I'll be glad to help if you want to develop a specific filter. .PP \fI\s-1FILTER_ARGUMENT\s0\fR .IX Subsection "FILTER_ARGUMENT" .PP it is possible to pass arguments to your filter, passing a reference allows you to modify the arguments when the filter is run (that happens for each node). .PP .Vb 4 \& sub SomeSub \& { \& my $counter = 0 ; \& my $data_structure = {.....} ; \& \& DumpTree($data_structure, \*(Aqtitle\*(Aq, FILTER => \e&CountNodes, FILTER_ARGUMENT => \e$counter) ; \& \& print "\e$counter = $counter\en" ; \& } \& \& sub CountNodes \& { \& my ($structure, $level, $path, $nodes_to_display, $setup, $counter) = @_ ; \& $$counter++ ; # remember to pass references if you want them to be changed by the filter \& \& return(DefaultNodesToDisplay($structure)) ; \& } .Ve .PP \fIKey removal\fR .IX Subsection "Key removal" .PP Entries can be removed from the display by not returning their keys. .PP .Vb 4 \& my $s = {visible => \*(Aq\*(Aq, also_visible => \*(Aq\*(Aq, not_visible => \*(Aq\*(Aq} ; \& my $OnlyVisible = sub \& { \& my $s = shift ; \& \& if(\*(AqHASH\*(Aq eq ref $s) \& { \& return(\*(AqHASH\*(Aq, undef, grep {! /^not_visible/} keys %$s) ; \& } \& \& return(Data::TreeDumper::DefaultNodesToDisplay($s)) ; \& } \& \& DumpTree($s, \*(Aqtitle\*(Aq, FILTER => $OnlyVisible) ; .Ve .PP \fILabel changing\fR .IX Subsection "Label changing" .PP The label for a hash keys or an array index can be altered. This can be used to add visual information to the tree dump. Instead of returning the key name, return an array reference containing the key name and the label you want to display. You only need to return such a reference for the entries you want to change, thus a mix of scalars and array refs is acceptable. .PP .Vb 3 \& sub StarOnA \& { \& # hash entries matching /^a/i have \*(Aq*\*(Aq prepended \& \& my $tree = shift ; \& \& if(\*(AqHASH\*(Aq eq ref $tree) \& { \& my @keys_to_dump ; \& \& for my $key_name (keys %$tree) \& { \& if($key_name =~ /^a/i) \& { \& $key_name = [$key_name, "* $key_name"] ; \& } \& \& push @keys_to_dump, $key_name ; \& } \& \& return (\*(AqHASH\*(Aq, undef, @keys_to_dump) ; \& } \& \& return (Data::TreeDumper::DefaultNodesToDisplay($tree)) ; \& } \& \& print DumpTree($s, "Entries matching /^a/i have \*(Aq*\*(Aq prepended", FILTER => \e&StarOnA) ; .Ve .PP If you use an \s-1ANSI\s0 terminal, you can also change the color of the label. This can greatly improve the visual search time. See the \fIlabel coloring\fR example in \fIcolors.pl\fR. .PP \fIStructure replacement\fR .IX Subsection "Structure replacement" .PP It is possible to replace the whole data structure in a filter. This comes in handy when you want to display a \fI\*(L"worked\*(R"\fR version of the structure. You can even change the type of the data structure, for example changing an array to a hash. .PP .Vb 3 \& sub ReplaceArray \& { \& # replace arrays with hashes!!! \& \& my $tree = shift ; \& \& if(\*(AqARRAY\*(Aq eq ref $tree) \& { \& my $multiplication = $tree\->[0] * $tree\->[1] ; \& my $replacement = {MULTIPLICATION => $multiplication} ; \& return(\*(AqHASH\*(Aq, $replacement, keys %$replacement) ; \& } \& \& return (Data::TreeDumper::DefaultNodesToDisplay($tree)) ; \& } \& \& print DumpTree($s, \*(Aqreplace arrays with hashes!\*(Aq, FILTER => \e&ReplaceArray) ; .Ve .PP Here is a real life example. \fBTree::Simple\fR () allows one to build tree structures. The child nodes are not directly in the parent object (hash). Here is an unfiltered dump of a tree with seven nodes: .PP .Vb 10 \& Tree::Simple through Data::TreeDumper \& |\- _children \& | |\- 0 \& | | |\- _children \& | | | \`\- 0 \& | | | |\- _children \& | | | |\- _depth = 1 \& | | | |\- _node = 1.1 \& | | | \`\- _parent \& | | |\- _depth = 0 \& | | |\- _node = 1 \& | | \`\- _parent \& | |\- 1 \& | | |\- _children \& | | | |\- 0 \& | | | | |\- _children \& | | | | |\- _depth = 1 \& | | | | |\- _node = 2.1 \& | | | | \`\- _parent \& | | | |\- 1 \& | | | | |\- _children \& | | | | |\- _depth = 1 \& | | | | |\- _node = 2.1a \& | | | | \`\- _parent \& | | | \`\- 2 \& | | | |\- _children \& | | | |\- _depth = 1 \& | | | |\- _node = 2.2 \& | | | \`\- _parent \& | | |\- _depth = 0 \& | | |\- _node = 2 \& | | \`\- _parent \& | \`\- 2 \& | |\- _children \& | |\- _depth = 0 \& | |\- _node = 3 \& | \`\- _parent \& |\- _depth = \-1 \& |\- _node = 0 \& \`\- _parent = root .Ve .PP This is nice for the developer but not for a user wanting to oversee the node hierarchy. One of the possible filters would be: .PP .Vb 3 \& FILTER => sub \& { \& my $s = shift ; \& \& if(\*(AqTree::Simple\*(Aq eq ref $s) \& { \& my $counter = 0 ; \& \& return \& ( \& \*(AqARRAY\*(Aq \& , $s\->{_children} \& , map{[$counter++, $_\->{_node}]} @{$s\->{_children}} # index generation \& ) ; \& } \& \& return(Data::TreeDumper::DefaultNodesToDisplay($s)) ; \& } .Ve .PP Which would give this much more readable output: .PP .Vb 8 \& Tree::Simple through Data::TreeDumper2 \& |\- 1 \& | \`\- 1.1 \& |\- 2 \& | |\- 2.1 \& | |\- 2.1a \& | \`\- 2.2 \& \`\- 3 .Ve .PP What about counting the children nodes? The index generating code becomes: .PP .Vb 1 \& map{[$counter++, "$_\->{_node} [" . @{$_\->{_children}} . "]"]} @{$s\->{_children}} \& \& Tree::Simple through Data::TreeDumper4 \& |\- 1 [1] \& | \`\- 1.1 [0] \& |\- 2 [3] \& | |\- 2.1 [0] \& | |\- 2.1a [0] \& | \`\- 2.2 [0] \& \`\- 3 [0] .Ve .PP \fIFilter chaining\fR .IX Subsection "Filter chaining" .PP It is possible to chain filters. \fICreateChainingFilter\fR takes a list of filtering sub references. The filters must properly handle the third parameter passed to them. .PP Suppose you want to chain a filter that adds a star before each hash key label, with a filter that removes all (original) keys that match /^a/i. .PP .Vb 6 \& sub AddStar \& { \& my $s = shift ; \& my $level = shift ; \& my $path = shift ; \& my $keys = shift ; \& \& if(\*(AqHASH\*(Aq eq ref $s) \& { \& $keys = [keys %$s] unless defined $keys ; \& \& my @new_keys ; \& \& for (@$keys) \& { \& if(\*(Aq\*(Aq eq ref $_) \& { \& push @new_keys, [$_, "* $_"] ; \& } \& else \& { \& # another filter has changed the label \& push @new_keys, [$_\->[0], "* $_\->[1]"] ; \& } \& } \& \& return(\*(AqHASH\*(Aq, undef, @new_keys) ; \& } \& \& return(Data::TreeDumper::DefaultNodesToDisplay($s)) ; \& } ; \& \& sub RemoveA \& { \& my $s = shift ; \& my $level = shift ; \& my $path = shift ; \& my $keys = shift ; \& \& if(\*(AqHASH\*(Aq eq ref $s) \& { \& $keys = [keys %$s] unless defined $keys ; \& my @new_keys ; \& \& for (@$keys) \& { \& if(\*(Aq\*(Aq eq ref $_) \& { \& push @new_keys, $_ unless /^a/i ; \& } \& else \& { \& # another filter has changed the label \& push @new_keys, $_ unless $_\->[0] =~ /^a/i ; \& } \& } \& \& return(\*(AqHASH\*(Aq, undef, @new_keys) ; \& } \& \& return(Data::TreeDumper::DefaultNodesToDisplay($s)) ; \& } ; \& \& DumpTree($s, \*(AqChained filters\*(Aq, FILTER => CreateChainingFilter(\e&AddStar, \e&RemoveA)) ; .Ve .SS "Level Filters" .IX Subsection "Level Filters" It is possible to define one filter per specific level. If a filter for a specific level exists it is used instead of the global filter. .PP \&\s-1LEVEL_FILTERS\s0 => {1 => \e&FilterForLevelOne, 5 => \e&FilterForLevelFive ... } ; .SS "Type Filters" .IX Subsection "Type Filters" You can define filters for specific types of references. This filter type has the highest priority. .PP here's a very simple filter that will display the specified keys for the types .PP .Vb 10 \& print DumpTree \& ( \& $data, \& \*(Aqtitle\*(Aq, \& TYPE_FILTERS => \& { \& \*(AqConfig::Hierarchical\*(Aq => sub {\*(AqHASH\*(Aq, undef, qw(CATEGORIES) }, \& \*(AqPBS2::Node\*(Aq => sub {\*(AqHASH\*(Aq, undef, qw(CONFIG DEPENDENCIES MATCH) },, \& } \& ) ; .Ve .SS "Using filters as iterators" .IX Subsection "Using filters as iterators" You can iterate through your data structures and display data yourself, manipulate the data structure, or do a search. While iterating through the data structure, you can prune arbitrary branches to speed processing up. .PP .Vb 4 \& # this example counts the nodes in a tree (hash based) \& # a node is counted if it has a \*(Aq_\|_NAME\*(Aq key \& # any field that starts with \*(Aq_\|_\*(Aq is considered private and we prune so we don\*(Aqt recurse in it \& # anything that is not a hash (the part of the tree that interests us in this case) is pruned \& \& my $number_of_nodes_in_the_dependency_tree = 0 ; \& my $node_counter = \& sub \& { \& my $tree = shift ; \& if(\*(AqHASH\*(Aq eq ref $tree && exists $tree\->{_\|_NAME}) \& { \& $number_of_nodes_in_the_dependency_tree++ if($tree\->{_\|_NAME} !~ /^_\|_/) ; \& \& return(\*(AqHASH\*(Aq, $tree, grep {! /^_\|_/} keys %$tree) ; # prune to run faster \& } \& else \& { \& return(\*(AqSCALAR\*(Aq, 1) ; # prune \& } \& } ; \& \& DumpTree($dependency_tree, \*(Aq\*(Aq, NO_OUTPUT => 1, FILTER => $node_counter) ; .Ve .PP See the example under \s-1FILTER\s0 which passes arguments through Data::TreeDumper instead for using a closure as above .SS "Start level" .IX Subsection "Start level" This configuration option controls whether the tree trunk is displayed or not. .PP \&\s-1START_LEVEL\s0 => 1: .PP .Vb 10 \& $tree: \& |\- A [H1] \& | |\- a [H2] \& | |\- bbbbbb = CODE(0x8139fa0) [C3] \& | |\- c123 [C4 \-> C3] \& | \`\- d [R5] \& | \`\- REF(0x8139fb8) [R5 \-> C3] \& |\- ARRAY [A6] \& | |\- 0 [S7] = element_1 \& | |\- 1 [S8] = element_2 .Ve .PP \&\s-1START_LEVEL\s0 => 0: .PP .Vb 10 \& $tree: \& A [H1] \& |\- a [H2] \& |\- bbbbbb = CODE(0x8139fa0) [C3] \& |\- c123 [C4 \-> C3] \& \`\- d [R5] \& \`\- REF(0x8139fb8) [R5 \-> C3] \& ARRAY [A6] \& |\- 0 [S7] = element_1 \& |\- 1 [S8] = element_2 .Ve .SS "\s-1ASCII\s0 vs \s-1ANSI\s0" .IX Subsection "ASCII vs ANSI" You can direct Data::TreeDumper to output \s-1ANSI\s0 codes instead of \s-1ASCII\s0 characters. The display will be much nicer but takes slightly longer (not significant for small data structures). .PP .Vb 1 \& USE_ASCII => 0 # will use ANSI codes instead .Ve .SS "Display number of elements" .IX Subsection "Display number of elements" .Vb 1 \& DISPLAY_NUMBER_OF_ELEMENTS => 1 .Ve .PP When set, the number of elements of every array and hash is displayed (not for objects based on hashes and arrays). .SS "Maximum depth of the dump" .IX Subsection "Maximum depth of the dump" Controls the depth beyond which which we don't recurse into a structure. Default is \-1, which means there is no maximum depth. This is useful for limiting the amount of data displayed. .PP .Vb 1 \& MAX_DEPTH => 1 .Ve .SS "Number of elements not displayed because of maximum depth limit" .IX Subsection "Number of elements not displayed because of maximum depth limit" Data::TreDumper will display the number of elements a hash or array has but that can not be displayed because of the maximum depth setting. .PP .Vb 1 \& DISPLAY_NUMBER_OF_ELEMENTS_OVER_MAX_DEPTH => 1 .Ve .SS "Indentation" .IX Subsection "Indentation" The value of \fI\s-1INDENTATION\s0\fR will be prepended to every line of the tree dump. .PP .Vb 1 \& INDENTATION => \*(Aq \*(Aq ; .Ve .SH "Custom glyphs" .IX Header "Custom glyphs" You can change the glyphs used by \fBData::TreeDumper\fR. .PP .Vb 1 \& DumpTree(\e$s, \*(Aqs\*(Aq, , GLYPHS => [\*(Aq. \*(Aq, \*(Aq. \*(Aq, \*(Aq. \*(Aq, \*(Aq. \*(Aq]) ; \& \& # output \& s \& . REF(0x813da3c) [H1] \& . . A [H2] \& . . . a [H3] \& . . . b [H4] \& . . . . a = 0 [S5] \& . . . . b = 1 [S6] \& . . . . c [H7] \& . . . . . a = 1 [S8] .Ve .PP Four glyphs must be given. They replace the standard glyphs ['| ', '|\- ', '`\- ', ' ']. It is also possible to set the package variable \fB\f(CB$Data::TreeDumper::Glyphs\fB\fR. \fB\s-1USE_ASCII\s0\fR should be set, which it is by default. .SH "Level numbering and tagging" .IX Header "Level numbering and tagging" Data::TreeDumper can prepend the level of the current line to the tree glyphs. This can be very useful when searching in tree dump either visually or with a pager. .PP .Vb 2 \& NUMBER_LEVELS => 2 \& NUMBER_LEVELS => \e&NumberingSub .Ve .PP \&\s-1NUMBER_LEVELS\s0 can be assigned a number or a sub reference. When assigned a number, Data::TreeDumper will use that value to define the width of the field where the level is displayed. For more control, you can define a sub that returns a string to be displayed on the left side of the tree glyphs. The example below tags all the nodes whose level is zero. .PP .Vb 1 \& print DumpTree($s, "Level numbering", NUMBER_LEVELS => 2) ; \& \& sub GetLevelTagger \& { \& my $level_to_tag = shift ; \& \& sub \& { \& my ($element, $level, $setup) = @_ ; \& \& my $tag = "Level $level_to_tag => "; \& \& if($level == 0) \& { \& return($tag) ; \& } \& else \& { \& return(\*(Aq \*(Aq x length($tag)) ; \& } \& } ; \& } \& \& print DumpTree($s, "Level tagging", NUMBER_LEVELS => GetLevelTagger(0)) ; .Ve .SH "Level coloring" .IX Header "Level coloring" Another way to enhance the output for easier searching is to colorize it. Data::TreeDumper can colorize the glyph elements or whole levels. If your terminal supports \s-1ANSI\s0 codes, using Term::ANSIColors and Data::TreeDumper together can greatly ease the reading of large dumps. See the examples in '\fBcolor.pl\fR'. .PP .Vb 1 \& COLOR_LEVELS => [\e@color_codes, $reset_code] .Ve .PP When passed an array reference, the first element is an array containing coloring codes. The codes are indexed with the node level modulo the size of the array. The second element is used to reset the color after the glyph is displayed. If the second element is an empty string, the glyph and the rest of the level is colorized. .PP .Vb 1 \& COLOR_LEVELS => \e&LevelColoringSub .Ve .PP If \s-1COLOR_LEVEL\s0 is assigned a sub, the sub is called for each glyph element. It is passed the following elements: .IP "1 \- the nodes depth (this allows you to selectively display elements at a certain depth)" 2 .IX Item "1 - the nodes depth (this allows you to selectively display elements at a certain depth)" .PP It should return a coloring code and a reset code. If you return an empty string for the reset code, the whole node is displayed using the last glyph element color. .PP If level numbering is on, it is also colorized. .SH "Wrapping" .IX Header "Wrapping" \&\fBData::TreeDumper\fR uses the Text::Wrap module to wrap your data to fit your display. Entries can be wrapped multiple times so they snugly fit your screen. .PP .Vb 8 \& | | |\- 1 [S21] = 1 \& | | \`\- 2 [S22] = 2 \& | \`\- 3 [OH23 \-> R17] \& |\- ARRAY_ZERO [A24] \& |\- B [S25] = scalar \& |\- Long_name Long_name Long_name Long_name Long_name Long_name \& | Long_name Long_name Long_name Long_name Long_name Long_name \& | Long_name Long_name Long_name Long_name Long_name [S26] = 0 .Ve .PP You can direct \s-1DTD\s0 to not wrap your text by setting \fB\s-1NO_WRAP\s0 =\fR 1>. .SS "\s-1WRAP_WIDTH\s0" .IX Subsection "WRAP_WIDTH" if this option is set, \fBData::TreeDumper\fR will use it instead for the console width. .SH "Custom Rendering" .IX Header "Custom Rendering" \&\fBData::TreeDumper\fR has a plug-in interface for other rendering formats. The renderer callbacks are set by overriding the native renderer. Thanks to Stevan Little author of Tree::Simple::View for getting \&\fBData::TreeDumper\fR on this track. Check \fBData::TreeDumper::Renderer::DHTML\fR. .PP .Vb 9 \& DumpTree \& ( \& $s \& , \*(AqTree\*(Aq \& , RENDERER => \& { \& BEGIN => \e&RenderDhtmlBegin \& , NODE => \e&RenderDhtmlNode \& , END => \e&RenderDhtmlEnd \& \& # data needed by the renderer \& , PREVIOUS_LEVEL => \-1 \& , PREVIOUS_ADDRESS => \*(AqROOT\*(Aq \& } \& ) ; .Ve .SS "Callbacks" .IX Subsection "Callbacks" .IP "\(bu" 2 {\s-1RENDERER\s0}{\s-1BEGIN\s0} is called before the traversal of the data structure starts. This allows you to setup the document (ex:: html header). .Sp my ($title, \f(CW$type_address\fR, \f(CW$element\fR, \f(CW$size\fR, \f(CW$perl_address\fR, \f(CW$setup\fR) = \f(CW@_\fR ; .RS 2 .ie n .IP "1 $title" 4 .el .IP "1 \f(CW$title\fR" 4 .IX Item "1 $title" .PD 0 .ie n .IP "2 $type_address" 4 .el .IP "2 \f(CW$type_address\fR" 4 .IX Item "2 $type_address" .ie n .IP "3 $element" 4 .el .IP "3 \f(CW$element\fR" 4 .IX Item "3 $element" .ie n .IP "4 $perl_size" 4 .el .IP "4 \f(CW$perl_size\fR" 4 .IX Item "4 $perl_size" .ie n .IP "5 $perl_address" 4 .el .IP "5 \f(CW$perl_address\fR" 4 .IX Item "5 $perl_address" .ie n .IP "6 $setup" 4 .el .IP "6 \f(CW$setup\fR" 4 .IX Item "6 $setup" .RE .RS 2 .RE .IP "\(bu" 2 .PD {\s-1RENDERER\s0}{\s-1NODE\s0} is called for each node in the data structure. The following arguments are passed to the callback .RS 2 .ie n .IP "1 $element" 4 .el .IP "1 \f(CW$element\fR" 4 .IX Item "1 $element" .PD 0 .ie n .IP "2 $level" 4 .el .IP "2 \f(CW$level\fR" 4 .IX Item "2 $level" .ie n .IP "3 $is_terminal (whether a deeper structure will follow or not)" 4 .el .IP "3 \f(CW$is_terminal\fR (whether a deeper structure will follow or not)" 4 .IX Item "3 $is_terminal (whether a deeper structure will follow or not)" .ie n .IP "4 $previous_level_separator (\s-1ASCII\s0 separators before this node)" 4 .el .IP "4 \f(CW$previous_level_separator\fR (\s-1ASCII\s0 separators before this node)" 4 .IX Item "4 $previous_level_separator (ASCII separators before this node)" .ie n .IP "5 $separator (\s-1ASCII\s0 separator for this element)" 4 .el .IP "5 \f(CW$separator\fR (\s-1ASCII\s0 separator for this element)" 4 .IX Item "5 $separator (ASCII separator for this element)" .ie n .IP "6 $element_name" 4 .el .IP "6 \f(CW$element_name\fR" 4 .IX Item "6 $element_name" .ie n .IP "7 $element_value" 4 .el .IP "7 \f(CW$element_value\fR" 4 .IX Item "7 $element_value" .ie n .IP "8 $td_address (address of the element, Ex: C12 or H34. Unique for each element)" 4 .el .IP "8 \f(CW$td_address\fR (address of the element, Ex: C12 or H34. Unique for each element)" 4 .IX Item "8 $td_address (address of the element, Ex: C12 or H34. Unique for each element)" .ie n .IP "9 $link_address (link to another element, may be undef)" 4 .el .IP "9 \f(CW$link_address\fR (link to another element, may be undef)" 4 .IX Item "9 $link_address (link to another element, may be undef)" .ie n .IP "10 $perl_size (size of the element in bytes, see option \fB\s-1DISPLAY_PERL_SIZE\s0\fR)" 4 .el .IP "10 \f(CW$perl_size\fR (size of the element in bytes, see option \fB\s-1DISPLAY_PERL_SIZE\s0\fR)" 4 .IX Item "10 $perl_size (size of the element in bytes, see option DISPLAY_PERL_SIZE)" .ie n .IP "11 $perl_address (address (physical) of the element, see option \fB\s-1DISPLAY_PERL_ADDRESS\s0\fR)" 4 .el .IP "11 \f(CW$perl_address\fR (address (physical) of the element, see option \fB\s-1DISPLAY_PERL_ADDRESS\s0\fR)" 4 .IX Item "11 $perl_address (address (physical) of the element, see option DISPLAY_PERL_ADDRESS)" .ie n .IP "12 $setup (the dumper's settings)" 4 .el .IP "12 \f(CW$setup\fR (the dumper's settings)" 4 .IX Item "12 $setup (the dumper's settings)" .RE .RS 2 .RE .IP "\(bu" 2 .PD {\s-1RENDERER\s0}{\s-1END\s0} is called after the last node has been processed. .IP "\(bu" 2 {\s-1RENDERER\s0}{ ... } Arguments to the renderer can be stored within the {\s-1RENDERER\s0} hash. .SS "Renderer modules" .IX Subsection "Renderer modules" Renderers should be defined in modules under \fBData::TreeDumper::Renderer\fR and should define a function called \fIGetRenderer\fR. \fIGetRenderer\fR can be passed whatever arguments the developer wishes. It is acceptable for the modules to also export a specific sub. .PP .Vb 3 \& print DumpTree($s, \*(AqTree\*(Aq, Data::TreeDumper::Renderer::DHTML::GetRenderer()) ; \& or \& print DumpTree($s, \*(AqTree\*(Aq, GetDhtmlRenderer()) ; .Ve .PP If \fB{\s-1RENDERER\s0}\fR is set to a scalar, \fBData::TreeDumper\fR will load the specified module if it exists. \fIGetRenderer\fR will be called without arguments. .PP .Vb 1 \& print DumpTree($s, \*(AqTree\*(Aq, RENDERER => \*(AqDHTML\*(Aq) ; .Ve .PP If \fB{\s-1RENDERER\s0}{\s-1NAME\s0}\fR is set to a scalar, \fBData::TreeDumper\fR will load the specified module if it exists. \fIGetRenderer\fR will be called without arguments. Arguments to the renderer can either be passed to the GetRenderer sub or as elements in the {\s-1RENDERER\s0} hash. .PP .Vb 1 \& print DumpTree($s, \*(AqTree\*(Aq, RENDERER => {NAME => \*(AqDHTML\*(Aq, STYLE => \e$style) ; .Ve .SH "Zero-width console" .IX Header "Zero-width console" When no console exists, while redirecting to a file for example, Data::TreeDumper uses the variable \&\fB\s-1VIRTUAL_WIDTH\s0\fR instead. Default is 120. .PP .Vb 1 \& VIRTUAL_WIDTH => 120 ; .Ve .SH "OVERRIDE list" .IX Header "OVERRIDE list" .IP "\(bu" 2 \&\s-1COLOR_LEVELS\s0 .IP "\(bu" 2 \&\s-1DISPLAY_ADDRESS\s0 .IP "\(bu" 2 \&\s-1DISPLAY_PATH\s0 .IP "\(bu" 2 \&\s-1DISPLAY_PERL_SIZE\s0 .IP "\(bu" 2 \&\s-1DISPLAY_ROOT_ADDRESS\s0 .IP "\(bu" 2 \&\s-1DISPLAY_PERL_ADDRESS\s0 .IP "\(bu" 2 \&\s-1FILTER\s0 .IP "\(bu" 2 \&\s-1GLYPHS\s0 .IP "\(bu" 2 \&\s-1INDENTATION\s0 .IP "\(bu" 2 \&\s-1LEVEL_FILTERS\s0 .IP "\(bu" 2 \&\s-1MAX_DEPTH\s0 .IP "\(bu" 2 \&\s-1DISPLAY_NUMBER_OF_ELEMENTS_OVER_MAX_DEPTH\s0 .IP "\(bu" 2 \&\s-1NUMBER_LEVELS\s0 .IP "\(bu" 2 \&\s-1QUOTE_HASH_KEYS\s0 .IP "\(bu" 2 \&\s-1DISPLAY_NO_VALUE\s0 .IP "\(bu" 2 \&\s-1QUOTE_VALUES\s0 .IP "\(bu" 2 \&\s-1REPLACEMENT_LIST\s0 .IP "\(bu" 2 \&\s-1START_LEVEL\s0 .IP "\(bu" 2 \&\s-1USE_ASCII\s0 .IP "\(bu" 2 \&\s-1WRAP_WIDTH\s0 .IP "\(bu" 2 \&\s-1VIRTUAL_WIDTH\s0 .IP "\(bu" 2 \&\s-1NO_OUTPUT\s0 .IP "\(bu" 2 \&\s-1DISPLAY_OBJECT_TYPE\s0 .IP "\(bu" 2 \&\s-1DISPLAY_INHERITANCE\s0 .IP "\(bu" 2 \&\s-1DISPLAY_TIE\s0 .IP "\(bu" 2 \&\s-1DISPLAY_AUTOLOAD\s0 .SH "Interface" .IX Header "Interface" .SS "Package Data (à la Data::Dumper (as is the silly naming scheme))" .IX Subsection "Package Data (à la Data::Dumper (as is the silly naming scheme))" \fIConfiguration Variables\fR .IX Subsection "Configuration Variables" .PP .Vb 10 \& $Data::TreeDumper::Startlevel = 1 ; \& $Data::TreeDumper::Useascii = 1 ; \& $Data::TreeDumper::Maxdepth = \-1 ; \& $Data::TreeDumper::Indentation = \*(Aq\*(Aq ; \& $Data::TreeDumper::Virtualwidth = 120 ; \& $Data::TreeDumper::Displayrootaddress = 0 ; \& $Data::TreeDumper::Displayaddress = 1 ; \& $Data::TreeDumper::Displaypath = 0 ; \& $Data::TreeDumper::Displayobjecttype = 1 ; \& $Data::TreeDumper::Displayinheritance = 0 ; \& $Data::TreeDumper::Displaytie = 0 ; \& $Data::TreeDumper::Displayautoload = 0 ; \& $Data::TreeDumper::Displayperlsize = 0 ; \& $Data::TreeDumper::Displayperladdress = 0 ; \& $Data::TreeDumper::Filter = \e&FlipEverySecondOne ; \& $Data::TreeDumper::Levelfilters = {1 => \e&Filter_1, 5 => \e&Filter_5} ; \& $Data::TreeDumper::Numberlevels = 0 ; \& $Data::TreeDumper::Glyphs = [\*(Aq| \*(Aq, \*(Aq|\- \*(Aq, \*(Aq\`\- \*(Aq, \*(Aq \*(Aq] ; \& $Data::TreeDumper::Colorlevels = undef ; \& $Data::TreeDumper::Nooutput = 0 ; # generate an output \& $Data::TreeDumper::Quotehashkeys = 0 ; \& $Data::TreeDumper::Displaycallerlocation = 0 ; .Ve .PP \fI\s-1API\s0\fR .IX Subsection "API" .PP \&\fBPrintTree\fRprints on \s-1STDOUT\s0 the output of \fBDumpTree\fR. .PP \&\fBDumpTree\fR uses the configuration variables defined above. It takes the following arguments: .IP "[1] structure_to_dump" 2 .IX Item "[1] structure_to_dump" .PD 0 .IP "[2] title, a string to prepended to the tree (optional)" 2 .IX Item "[2] title, a string to prepended to the tree (optional)" .IP "[3] overrides (optional)" 2 .IX Item "[3] overrides (optional)" .PD .PP .Vb 1 \& print DumpTree($s, "title", MAX_DEPTH => 1) ; .Ve .PP \&\fBDumpTrees\fR uses the configuration variables defined above. It takes the following arguments .IP "[1] One or more array references containing" 2 .IX Item "[1] One or more array references containing" .RS 2 .PD 0 .IP "[a] structure_to_dump" 4 .IX Item "[a] structure_to_dump" .IP "[b] title, a string to prepended to the tree (optional)" 4 .IX Item "[b] title, a string to prepended to the tree (optional)" .IP "[c] overrides (optional)" 4 .IX Item "[c] overrides (optional)" .RE .RS 2 .RE .IP "[2] overrides (optional)" 2 .IX Item "[2] overrides (optional)" .PD .PP .Vb 7 \& print DumpTrees \& ( \& [$s, "title", MAX_DEPTH => 1] \& , [$s2, "other_title", DISPLAY_ADDRESS => 0] \& , USE_ASCII => 1 \& , MAX_DEPTH => 5 \& ) ; .Ve .SH "Bugs" .IX Header "Bugs" None that I know of in this release but plenty, lurking in the dark corners, waiting to be found. .SH "Examples" .IX Header "Examples" Four examples files are included in the distribution. .PP \&\fIusage.pl\fR shows you how you can use \fBData::TreeDumper\fR. .PP \&\fIfilters.pl\fR shows you how to do advance filtering. .PP \&\fIcolors.pl\fR shows you how to colorize a dump. .PP \&\fItry_it.pl\fR is meant as a scratch pad for you to try \fBData::TreeDumper\fR. .SH "DEPENDENCY" .IX Header "DEPENDENCY" \&\fBText::Wrap\fR. .PP \&\fBTerm::Size\fR or \fBWin32::Console\fR. .PP Optional \fBDevel::Size\fR if you want Data::TreeDumper to show perl sizes for the tree elements. .SH "EXPORT" .IX Header "EXPORT" \&\fIDumpTree\fR, \fIDumpTrees\fR and \fICreateChainingFilter\fR. .SH "AUTHOR" .IX Header "AUTHOR" Khemir Nadim ibn Hamouda. .PP Thanks to Ed Avis for showing interest and pushing me to re-write the documentation. .PP .Vb 4 \& Copyright (c) 2003\-2006 Nadim Ibn Hamouda el Khemir. All rights \& reserved. This program is free software; you can redis\- \& tribute it and/or modify it under the same terms as Perl \& itself. .Ve .PP If you find any value in this module, mail me! All hints, tips, flames and wishes are welcome at . .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fBData::TreeDumper::00\fR. \fBData::Dumper\fR. .PP \&\fBData::TreeDumper::Renderer::DHTML\fR. .PP \&\fBDevel::Size::Report\fR.\fBDevel::Size\fR. .PP \&\fB\s-1PBS\s0\fR: the Perl Build System from which \fBData::TreeDumper\fR was extracted. .SH "POD ERRORS" .IX Header "POD ERRORS" Hey! \fBThe above document had some coding errors, which are explained below:\fR .IP "Around line 806:" 4 .IX Item "Around line 806:" =pod directives shouldn't be over one line long! Ignoring all 7 lines of content