NAME
Data::TreeDraw - Graphical representation of nested data structures.
VERSION
This document describes Data::TreeDraw version 0.0.3
DESCRIPTION
While this module was written for me to visualise the internal structure of Perl5 Objects I was developing it should serve for any data-structure where you need to quickly analyse, understand and check the internal structure and values and more importantly access it - see "USEFUL EXAMPLE".
While there are a number of great programs out there for Dumping and visualising heavily-nested and data-rich data-structures these can often be overwhelming and hard to read - this modules aims to address these issues by not only giving a very simple interface for drawing clear branching structures but also a number of features that allow data-rich features e.g. long Lists and List-of-Lists to be printed more naturally and succinctly for interpretation - see "Long Arrays" and "Lists-of-Lists" in "OVERVIEW" (See "OVERVIEW" and "OPTIONS" for a comprehensive list of features).
Even more tricky than interpreting data-rich structures in heavily nested references can be the process of finding the exact combination of array-elements and hash-keys to use to dereference/access a particular ARRAY ref, SCALAR value etc. - often requiring that you manually back-trace over a dumped structure to find the specific combination to use. The notation
option of this module (defaults to on - see "notation" in "OVERVIEW").
Additionally, the output may be restricted in many ways including: printing only branches within the data-structure that match a specific hash key value (see "HASH key lookup" in "OVERVIEW"), printing only those SCALAR values matching a specific string value (see "SCALAR value lookup" in "OVERVIEW"), print only branches with internal nesting levels higher or lower than a specific level (see "Maximum printing depth" and "Minimum printing depth" in "OVERVIEW").
Alternatively you may add to the output: If you have object references within your nested structure but want the tree branching to carry on recursing into them so as to see their internals use the unwrap_object
option (see "Object recursion" in "OVERVIEW"). If you want a object method introspection as implement by the Class::MOP module use the object_methods
option (see "Method introspection for objects" in "OVERVIEW").
This module was written by me, for me, and so internally may be a bit esoteric. If there is significant interest I will improve and expand it.
SYNOPSIS
Create suitable structure.
# Create a Code Reference.
my $c_ref = sub { print q{blah}; $_->[0] };
# Create a heavily nested set of references.
my $r_ref = \\\\\[q{pink},321];
# Create an object of type HASH;
my $pca->{g} = 4;
bless $pca, q{Some::Class};
# Create a GLOB.
*f = *g;
# Create a nested data structure with various typed of nested data e.g. defined and undefined SCALAR, ARRAY, HASH and REFs
my $a = [ q{hi}, q{there}, [ q{i}, q{am} ], 1, { r => \\q{} }, { r => { y => 3 }, t => [ { r => *g } ] }, $c_ref, *f, $r_ref, q{nine}, [ [ 3,q{----3-----}, 3 ],
[3, 3, 3, 3, ], [ 3, 3, 3, ] ], 1, \\\{ r => \[] }, \\\q{foo}, { g => 2, t => 2, y => q{------2-------}, r => [3], step => { the => q{blah} }, o => [ [4]
] }, [ [ [q{--4--}]] , [ [ 4, 4, q{-4-}, q{--4--} ] ], 2, [3] ], \\\q{}, 1, [ [3 , 3], 2, $pca] , 1, { g => [ 3, 3, q{blah}, 3, 3, 3 ] } ,[ [ 3 ] ], $a, undef ];
# Create cyclic references.
my $b = [$a];
my $c = [$b];
$a->[2][3] = $c;
$a->[2][2] = $a;
# Make our nested data structure an object
bless $a, q{Other::Class};
Use module and call draw
routine on structure.
use Data::TreeDraw;
draw($a);
Prints:
Method called from Package 'main' on Blessed Object of type 'ARRAY' and Class 'Other::Class'.
ARRAY REFERENCE (0)
|
|__SCALAR = 'hi' (1) [ '->[0]' ]
|
|__SCALAR = 'there' (1) [ '->[1]' ]
|
|__ARRAY REFERENCE (1) [ '->[2]' ]
| |
| |__SCALAR = 'i' (2) [ '->[2][0]' ]
| |
| |__SCALAR = 'am' (2) [ '->[2][1]' ]
| |
| |__CYCLIC REFERENCE (2) [ '->[2][2]' ]
| |
| |__ARRAY REFERENCE (2) [ '->[2][3]' ]
| |
| |__ARRAY REFERENCE (3) [ '->[2][3][0]' ]
| |
| |__CYCLIC REFERENCE (4) [ '->[2][3][0][0]' ]
|
|__SCALAR = '1' (1) [ '->[3]' ]
|
|__HASH REFERENCE (1) [ '->[4]' ]
| |
| |__'r'=>REFERENCE-TO-REFERENCE (2)
| |
| |__SCALAR REFERENCE (3)
| |
| |__SCALAR = '' [EMPTY STRING] (4)
|
|__HASH REFERENCE (1) [ '->[5]' ]
| |
| |__'r'=>HASH REFERENCE (2) [ '->[5]{r}' ]
| | |
| | |__'y'=>SCALAR = '3' (3) [ '->[5]{r}{y}' ]
| |
| |__'t'=>ARRAY REFERENCE (2) [ '->[5]{t}' ]
| |
| |__HASH REFERENCE (3) [ '->[5]{t}[0]' ]
| |
| |__'r'=>GLOB = '*main::g' (4) [ '->[5]{t}[0]{r}' ]
|
etc.
USEFUL EXAMPLE
A simple example to demonstrate some of the features of this module is giving with a simple database lookup using DBI. We want to extract usernames, passwords, addresses and email addresses of all the entries within a table in a single ARRAY reference and modify it.
use DBI;
# connect to DB etc.
my $sql = q{select username, password, address, email from some_table};
my $db_as_a_ref = $dbh->selectall_hashref($sql);
By calling the program on the generated ARRAY reference we can look at the structure:
draw($db_as_h_ref);
This prints something like:
ARRAY REFERENCE (0)
|
|__ARRAY REFERENCE (1) [ '->[0]' ]
| |
| |__SCALAR = '1' (2) [ '->[0][0]' ]
| |
| |__SCALAR = 'user0' (2) [ '->[0][1]' ]
| |
| |__SCALAR = 'password0' (2) [ '->[0][2]' ]
| |
| |__SCALAR = 'address' (2) [ '->[0][3]' ]
| |
| |__SCALAR = 'user0@blah.net' (2) [ '->[0][4]' ]
|
lots more entries...
|
|__ARRAY REFERENCE (1) [ '->[13]' ]
| |
| |__SCALAR = '14' (2) [ '->[13][0]' ]
| |
| |__SCALAR = 'Dan' (2) [ '->[13][1]' ]
| |
| |__SCALAR = 'Not telling' (2) [ '->[13][2]' ]
| |
| |__SCALAR = 'Rio de Janeiro, Brasil' (2) [ '->[13][3]' ]
| |
| |__SCALAR = 'dsth@cpan.net' (2) [ '->[13][4]' ]
|
lots more entries...
First we immediately see the internal structure of the entries - namely that the passes reference was an ARRAY reference and that each individual entry is simple another nested ARRAY reference directly within this top level ARRAY reference (i.e. the nesting level of every element is given in parenthesis to the side of each entry. Next, we scroll down to my entry (shown by SCALAR value 'Dan' with nesting level 2 within one of these nested ARRAY references at nesting level 1) and see my address ('Rio de Janeiro, Brasil'. I need to change my address within this structure to 'NY, USA'. To do this I simply append the arrow operator dereferencing notation given within the square brackets to the right of the entry. Thus to change my address I immediately know that I need to use ->[13][3] dereferencing notation. Thus we change my address:
$db_as_h_ref->->[13][3] = q{NY, USA};
Perhaps I didn't want all the other information in the data-structure as I just want to change my name. We use the scalar_val
option.
draw($db_as_h_ref, { scalar_val => 'Dan' });
This prints just:
SCALAR value 'Dan' found at indentation level '2':
| |__SCALAR = 'Dan' (2) [ '->[13][1]' ]
| |
SCALAR value 'Dan' found 1 times in nested data structure.
So we immediately change my name:
$db_as_h_ref->[13][1] = q{Daniel};
Instead of passing the database entries as an ARRAY reference we may have used a HASH reference:
my $sql = q{select username, password, address, email from some_table};
my $db_as_h_ref = $dbh->selectall_hashref($sql, q{username});
In this case when we use the basic draw
routine we obtain:
HASH REFERENCE (0)
|
|__'user33'=>HASH REFERENCE (1) [ '->{user33}' ]
| |
| |__'email'=>SCALAR = 'user33@blah.net' (2) [ '->{user33}{email}' ]
| |
| |__'password'=>SCALAR = 'password33' (2) [ '->{user33}{password}' ]
| |
| |__'username'=>SCALAR = 'user33' (2) [ '->{user33}{username}' ]
|
lots more entries...
|
|__'Dan'=>HASH REFERENCE (1) [ '->{Dan}' ]
|
|__'email'=>SCALAR = 'dsth@cpan.net' (2) [ '->{Dan}{email}' ]
|
|__'password'=>SCALAR = 'Not telling' (2) [ '->{Dan}{password}' ]
|
|__'username'=>SCALAR = 'Dan' (2) [ '->{Dan}{username}'
First, the termination of the basic tree root descending from the passed HASH reference (with nesting level 0) shows that it´s the last of the entries in the structure. Again we immediately see the dereferencing notation we need to append to the passed structure. However, I really only wanted to see the entry corresponding to my details so we use the hash_key
option:
draw($db_as_h_ref, { hash_key => 'Dan' });
This simply prints:
HASH key 'Dan' found at indentation level '1':
|__'Dan'=>HASH REFERENCE (1) [ '->{Dan}' ]
|
|__'email'=>SCALAR = 'dsth@cpan.net' (2) [ '->{Dan}{email}' ]
|
|__'password'=>SCALAR = 'Not telling' (2) [ '->{Dan}{password}' ]
|
|__'username'=>SCALAR = 'Dan' (2) [ '->{Dan}{username}' ]
HASH key 'Dan' found 1 times in nested data structure.
OVERVIEW
The module exports a single sub-routine call draw
. Simply call this routine with the data structure you wish to print along with a HASH reference of any options you wish to pass - see "OPTIONS" and this section.
Tree Structure
All structures are displayed as a "clear" Tree-structure branching from a single root.
ARRAY REFERENCE (0)
|
|__SCALAR = 'hi' (1) [ '->[0]' ]
|
|__SCALAR = 'there' (1) [ '->[1]' ]
|
|__ARRAY REFERENCE (1) [ '->[2]' ]
| |
| |__SCALAR = 'i' (2) [ '->[2][0]' ]
| |
| |__SCALAR = 'am' (2) [ '->[2][1]' ]
| |
| |__CYCLIC REFERENCE (2) [ '->[2][2]' ]
| |
| |__ARRAY REFERENCE (2) [ '->[2][3]' ]
| |
| |__ARRAY REFERENCE (3) [ '->[2][3][0]' ]
etc.
Notation
The notation
option (defaults to on - with "1") results in the printing along side (in square-brackets) the specific REFERENCE or SCALAR value of the particular arrow-notation required to access/dereference that REFERENCE or SCALAR value e.g. "[ '->[4]{hash_key}[12]' ]" next to an ARRAY reference means that to dereference that particular value within the passed structure simply append "->[4]{hash_key}[12] " to the originally passed reference.
e.g. To access the above ARRAY reference printed as: "ARRAY REFERENCE (2) [ '->[2][3][0]' ]"
Simply use: $original_data_passed->[2][3][0];
Spacing
For a more compressed version of the print disable the spaces
options by setting it to "0" (this option is enabled by default). The above Tree is printed as:
ARRAY REFERENCE (0)
|__SCALAR = 'hi' (1) [ '->[0]' ]
|__SCALAR = 'there' (1) [ '->[1]' ]
|__ARRAY REFERENCE (1) [ '->[2]' ]
| |__SCALAR = 'i' (2) [ '->[2][0]' ]
| |__SCALAR = 'am' (2) [ '->[2][1]' ]
| |__CYCLIC REFERENCE (2) [ '->[2][2]' ]
| |__ARRAY REFERENCE (2) [ '->[2][3]' ]
| |__ARRAY REFERENCE (3) [ '->[2][3][0]' ]
| |__CYCLIC REFERENCE (4) [ '->[2][3][0][0]' ]
etc.
Indentation Level
The nesting/indentation level of ALL structures is printed along-side of the REFERENCE/SCALAR value in parenthesis:
e.g. SCALAR = 'some_value' (nesting_level/e.g.4)
Empty Strings
Any SCALAR value containing an empty string is printed as that e.g. '', but to ease distinguishing it from ' ' it additionally prints [EMPTY STRING] along-side.
e.g. SCALAR = '' [EMPTY STRING]
Long Arrays
With data-rich structures arrays may often have many elements. In such cases printing each SCALAR value within the array on a separate line makes reading the structure difficult:
ARRAY REFERENCE (2) [ '->[20]{g}' ]
|
|__SCALAR = 'val1' (3) [ '->[20]{g}[0]' ]
|
|__SCALAR = 'val2' (3) [ '->[20]{g}[1]' ]
|
|__SCALAR = 'val3' (3) [ '->[20]{g}[2]' ]
|
|__SCALAR = 'val4' (3) [ '->[20]{g}[3]' ]
etc.
The long_array
option (defaults to off - "0") over-rides this behaviour and instead arrays consisting of "just" SCALAR values are printed on a single-line. With this setting relatively short arrays are printed in full on a single line along with their length:
ARRAY REFERENCE (3) ---LONG_LIST_OF_SCALARS--- [ length = 4 ]: val1, val2, val3, val4 [ '->[20]{g}'
Longer arrays are printed in a similar fashion except that only the length and first 3 elements are printed (just to indicate the nature of the values stored by the array).
ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 4 ] e.g. 0..2: val1, val2, val3 [ '->[20]{g}' ]
You can switch the length of array that triggers these two behaviours using the array_length
option (defaults to 3). See OPTIONS for further info.
Lists-of-Lists.
In cases of Lists-of-lists the readability may suffer further - especially as these structures often correspond to 2-dim tables. Thus in cases of ARRAYS consisting uniquely of ARRAYS of SCALARS:
|__ARRAY REFERENCE (2) [ '->[10][0]' ]
| |
| |__SCALAR = '2' (3) [ '->[10][0][0]' ]
| |
| |__SCALAR = '3' (3) [ '->[10][0][1]' ]
| |
| |__SCALAR = '3' (3) [ '->[10][0][2]' ]
|
|__ARRAY REFERENCE (2) [ '->[10][1]' ]
| |
| |__SCALAR = '3' (3) [ '->[10][1][0]' ]
| |
| |__SCALAR = '3' (3) [ '->[10][1][1]' ]
| |
| |__SCALAR = '3' (3) [ '->[10][1][2]' ]
| |
| |__SCALAR = '3' (3) [ '->[10][1][3]' ]
|
|__ARRAY REFERENCE (2) [ '->[10][2]' ]
|
|__SCALAR = '3' (3) [ '->[10][2][0]' ]
|
|__SCALAR = '3' (3) [ '->[10][2][1]' ]
|
|__SCALAR = '3' (3) [ '->[10][2][2]' ]
The lol
option when set to "1" will replace these structures with (this option defaults to "0"):
ARRAY REFERENCE (1) ---LIST OF LISTS--- [ rows = 3 and longest nested list length = 4 ] [ '->[10]' ]
You may be interested in the particular values within these structures. In this case lol
set to "2" creates a temporary break in the tree-structure with a table of the values and their access values and an extension of their root:
ARRAY REFERENCE (1) ---LIST OF LISTS--- [ rows = 3 and longest nested list length = 4 ] [ '->[10]' ]
|
---
.----------+-------+-------+-------+-------.
| | ..[0] | ..[1] | ..[2] | ..[3] |
+----------+-------+-------+-------+-------+
| ..[0].. | '3' | '3' | '3' | --- |
| ..[1].. | '3' | '3' | '3' | '3' |
| ..[2].. | '3' | '3' | '3' | --- |
'----------+-------+-------+-------+-------'
---
|
This option may be used with the long_arrays
option in which case the lol
option takes precedence and above structure would be printed as above instead of:
|__ARRAY REFERENCE (1) [ '->[10]' ]
|
|__ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 3 ]: 3, 3, 3 [ '->[10][0]' ]
|
|__ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 4 ]: 3, 3, 3, 3 [ '->[10][1]' ]
|
|__ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 3 ]: 3, 3, 3 [ '->[10][2]' ]
SCALAR value lookup
You may just be interested in those parts of the structure with specific SCALAR values. In this case use the scalar_val
option. This will only print parts of the branching structure where SCALARS are encountered with a particular string value. See "scalar_val" in "OPTIONS" for usage.
SCALAR value 'blah' found at indentation level '3':
| | |__'the'=>SCALAR = 'blah' (3) [ '->[14]{step}{the}' ]
| |
SCALAR value 'blah' found at indentation level '3':
| |__SCALAR = 'blah' (3) [ '->[20]{g}[2]' ]
| |
SCALAR value 'blah' found 2 times in nested data structure.
HASH keys
As HASHES are simply unordered LISTs using a look up key HASH references are displayed just ARRAY references only with the hash key appended e.g.
|__HASH REFERENCE (1) [ '->[20]' ]
|
|__'hash_key'=>ARRAY REFERENCE (2) [ '->[20]{g}' ]
|
|__SCALAR = '3' (3) [ '->[20]{g}[0]' ]
HASH key lookup
You may just be interested in the values of a particular HASH entry. In this case using the hash_key
option you can start Tree printing from when that particular HASH key is encountered. See "hash_key" in "OPTIONS" for usage.
HASH key 'given_key' found at indentation level '5':
|__'given_key'=>REFERENCE-TO-REFERENCE (5)
| |
|__UNDEFINED ARRAY REFERENCE (6)
etc.
HASH key 'given_key' found 2 times in nested data structure.
Minimum printing depth
You may not be interested in values near the root of the structure. In which case you can set the min_depth
option (defaults to 0).
Starting print at depth 2.
| |__SCALAR = 'i' (2) [ '->[2][0]' ]
| |
| |__SCALAR = 'am' (2) [ '->[2][1]' ]
| |
| |__CYCLIC REFERENCE (2) [ '->[2][2]' ]
| |
| |__ARRAY REFERENCE (2) [ '->[2][3]' ]
| |
| |__ARRAY REFERENCE (3) [ '->[2][3][0]' ]
| |
| |__CYCLIC REFERENCE (4) [ '->[2][3][0][0]' ]
|
Indent decrementing to '1' below min_depth level of '2'
| |__'r'=>REFERENCE-TO-REFERENCE (2)
| |
| |__SCALAR REFERENCE (3)
| |
| |__SCALAR = '' [EMPTY STRING] (4)
|
Maximum printing depth
If you do not wish to view deeply nested structures you can set the max_depth
option (defaults to 10):
ARRAY REFERENCE (0)
|
|__SCALAR = 'hi' (1) [ '->[0]' ]
|
|__SCALAR = 'there' (1) [ '->[1]' ]
|
|__ARRAY REFERENCE (1) [ '->[2]' ]
| |
| |__SCALAR EXCEEDS MAX NESTING DEPTH (2)
| |
| |__SCALAR EXCEEDS MAX NESTING DEPTH (2)
| |
etc.
Object recursion
In cases where an object reference is pointed within the structure its class will be printed:
|__BLESSED OBJECT BELONGING TO CLASS: Statistics::PCA (2) [ '->[18][2]' ]
However, you may be wish to continue the recursion into the object. This can be done by setting the unwrap_object
option to "1" (defaults to "0"):
|
|__BLESSED OBJECT BELONGING TO CLASS: Statistics::PCA (3) ---RECURSING-INTO-OBJECT---
|
|
|__HASH REFERENCE (3)
|
etc.
Note: while the structure is indented further - the actual indentation level in parenthesis does not change - this is just aids the identification of the type of data-type of the object within the structure. Also as yet, the notation
option is not supported with the object_unwrap
option.
Method introspection for objects
You may additionally wish to introspect either the root structure or lower-level objects for their methods. This module can use the introspection facility of Class::MOP and print a formated table by setting the object_methods
option to "1" (as with lol
this temporarily breaks the tree structure):
|__BLESSED OBJECT BELONGING TO CLASS: Statistics::PCA (3) ---RECURSING-INTO-OBJECT---
|
---
.-----------------------------------------------------.
| Methods |
+-----------------------------------------------------+
| Statistics::PCA::_deep_copy_references |
| Statistics::PCA::print_eigenvectors |
| Statistics::PCA::_calculate_eigens_cephes |
| ... |
'-----------------------------------------------------'
---
|
|__HASH REFERENCE (3)
|
etc.
OPTIONS
All options are passed by hash reference:
draw($data, {max_depth => 3, unwrap_objects => 1, object_methods => 1} );
array_limit
Name: array_limit
Description: Used in conjunction with long_array option. Specifies the cutoff point for printing an entire array of SCALARS and just first few example elements.
Usage: draw($data, {array_limit => 6});
Values: 3-10.
Default: 5.
hash_key
Name: hash_key
Description: Allows printing of just those branches pointed to by a particular HASH key of interest within a structure.
Usage: draw($data, {hash_key => q{a_key_name});
Values: String.
Default: undef.
lol
Name: lol
Description: This option suppresses long-outputs given from Lists-of-Lists - see OVERVIEW.
Usage: draw($data, {lol => 2});
Values: 0, 1, 2.
Default: 0.
long_array
Name: long_array
Description: This option suppresses long-output from long arrays of SCALARS - see OVERVIEW.
Usage: draw($data, {long_array => 2});
Values: 0, 1.
Default: 0.
max_depth
Name: max_depth
Description: Specifies the maximum indentation/nesting depth to proceed to - see OVERVIEW.
Usage: draw($data, {max_depth => 6});
Values: 0-10.
Default: 10.
max_methods
Name: max_methods
Description: Used in conjunction with the object_methods option. Specifies the maximum number of object methods to print.
Usage: draw($data, {max_methods => 6});
Values: 1-100.
Default: 50.
min_depth
Name: min_depth
Description: Specifies the minimum indentation depth to start printing at - see OVERVIEW.
Usage: draw($data, {array_limit => 6});
Values: 0-9.
Default: 0.
notation
Name: notation
Description: This option enables or disables the automatic arrow "->" notation printing specifying how to dereference a particular entity within the structure.
Usage: draw($data, {notation => 0});
Values: 0, 1.
Default: 1.
object_methods
Name: object_methods
Description: Turns on object method introspection using the Class::MOP module.
Usage: draw($data, {object_methods => 1});
Values: 0, 1.
Default: 0.
spaces
Name: spaces
Description: This option enables and disables the printing of extra "branch" lines in the Tree structure for easier visualisation.
Usage: draw($data, {spaces => 0});
Values: 0, 1.
Default: 1.
scalar_val
Name: scalar_val
Description: Allows printing of just those SCALAR values possessing specific string values.
Usage: draw($data, {scalar_val => q{a_value_of_interest});
Values: String.
Default: undef.
unwrap_objects
Name: unwrap_objects
Description: This option causes the program to recurse into objects that fall within a data structure.
Usage: draw($data, {unwrap_objects => 1});
Values: 0, 1.
Default: 1.
borders
Name: borders - This option is not yet fully implemented.
Description:
Usage: Disabled atm as not yet fully implemented.
Values: "0", "1".
Default: "0" (off).
DEPENDENCIES
Scalar::Util => "1.22", Class::MOP => "0.95", Text::SimpleTable => "2.0", Carp => "1.08",
AUTHOR
Daniel S. T. Hughes <dsth@cantab.net>
SEE ALSO
Data::Dumper, Data::TreeDumper.
LICENCE AND COPYRIGHT
Copyright (c) 2009, Daniel S. T. Hughes <dsth@cantab.net>
. All rights reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.
DISCLAIMER OF WARRANTY
because this software is licensed free of charge, there is no warranty for the software, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide the software "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the software is with you. Should the software prove defective, you assume the cost of all necessary servicing, repair, or correction.
In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute the software as permitted by the above licence, be liable to you for damages, including any general, special, incidental, or consequential damages arising out of the use or inability to use the software (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the software to operate with any other software), even if such holder or other party has been advised of the possibility of such damages.
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 258:
Non-ASCII character seen before =encoding in 'it´s'. Assuming UTF-8