File: Object.pm

package info (click to toggle)
libdata-printer-perl 1.001000-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 732 kB
  • sloc: perl: 4,305; makefile: 7; sh: 1
file content (1498 lines) | stat: -rw-r--r-- 54,255 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
use strict;
use warnings;
use Data::Printer::Common;

package # hide from pause
    Data::Printer::Object::ClassOptions;
    sub parents            { $_[0]->{'parents'}            }
    sub linear_isa         { $_[0]->{'linear_isa'}         }
    sub universal          { $_[0]->{'universal'}          }
    sub expand             { $_[0]->{'expand'}             }
    sub stringify          { $_[0]->{'stringify'}          }
    sub show_reftype       { $_[0]->{'show_reftype'}       }
    sub show_overloads     { $_[0]->{'show_overloads'}     }
    sub show_methods       { $_[0]->{'show_methods'}       }
    sub sort_methods       { $_[0]->{'sort_methods'}       }
    sub show_wrapped       { $_[0]->{'show_wrapped'}       }
    sub inherited          { $_[0]->{'inherited'}          }
    sub format_inheritance { $_[0]->{'format_inheritance'} }
    sub parent_filters     { $_[0]->{'parent_filters'}     }
    sub internals          { $_[0]->{'internals'}          }
    sub new {
        my ($class, $params) = @_;
        my $self = {
            'linear_isa'     => Data::Printer::Common::_fetch_scalar_or_default($params, 'linear_isa', 'auto'),
            'show_reftype'   => Data::Printer::Common::_fetch_scalar_or_default($params, 'show_reftype', 0),
            'show_overloads' => Data::Printer::Common::_fetch_scalar_or_default($params, 'show_overloads', 1),
            'stringify'      => Data::Printer::Common::_fetch_scalar_or_default($params, 'stringify', 1),
            'expand'         => Data::Printer::Common::_fetch_scalar_or_default($params, 'expand', 1),
            'show_methods'   => Data::Printer::Common::_fetch_anyof(
                $params, 'show_methods', 'all', [qw(none all private public)]
            ),
            'inherited' => Data::Printer::Common::_fetch_anyof(
                $params, 'inherited', 'public', [qw(none all private public)]
            ),
            'format_inheritance' => Data::Printer::Common::_fetch_anyof(
                $params, 'format_inheritance', 'lines', [qw(string lines)]
            ),
            'parent_filters' => Data::Printer::Common::_fetch_scalar_or_default($params, 'parent_filters', 1),
            'universal'    => Data::Printer::Common::_fetch_scalar_or_default($params, 'universal', 0),
            'sort_methods' => Data::Printer::Common::_fetch_scalar_or_default($params, 'sort_methods', 1),
            'show_wrapped' => Data::Printer::Common::_fetch_scalar_or_default($params, 'show_wrapped', 1),
            'internals'    => Data::Printer::Common::_fetch_scalar_or_default($params, 'internals', 1),
            'parents'      => Data::Printer::Common::_fetch_scalar_or_default($params, 'parents', 1),
        };
        return bless $self, $class;
    }
1;

package Data::Printer::Object;
use Scalar::Util ();
use Data::Printer::Theme;
use Data::Printer::Filter::SCALAR; # also implements LVALUE
use Data::Printer::Filter::ARRAY;
use Data::Printer::Filter::HASH;
use Data::Printer::Filter::REF;
use Data::Printer::Filter::VSTRING;
use Data::Printer::Filter::GLOB;
use Data::Printer::Filter::FORMAT;
use Data::Printer::Filter::Regexp;
use Data::Printer::Filter::CODE;
use Data::Printer::Filter::GenericClass;

# create our basic accessors:
my @method_names =qw(
    name show_tainted show_unicode show_readonly show_lvalue show_refcount
    show_memsize memsize_unit print_escapes scalar_quotes escape_chars
    caller_info caller_message caller_message_newline caller_message_position
    string_max string_overflow string_preserve resolve_scalar_refs
    array_max array_overflow array_preserve hash_max hash_overflow
    hash_preserve unicode_charnames colored theme show_weak
    max_depth index separator end_separator class_method class hash_separator
    align_hash sort_keys quote_keys deparse return_value show_dualvar show_tied
    warnings arrows coderef_stub coderef_undefined
);
foreach my $method_name (@method_names) {
    no strict 'refs';
    *{__PACKAGE__ . "::$method_name"} = sub {
        $_[0]->{$method_name} = $_[1] if @_ > 1;
        return $_[0]->{$method_name};
    }
}
sub extra_config { $_[0]->{extra_config} }

sub current_depth { $_[0]->{_depth}   }
sub indent        { $_[0]->{_depth}++ }
sub outdent       { $_[0]->{_depth}-- }

sub newline {
    my ($self) = @_;
    return $self->{_linebreak}
         . (' ' x ($self->{_depth} * $self->{_current_indent}))
         . (' ' x $self->{_array_padding})
         ;
}

sub current_name {
    my ($self, $new_value) = @_;
    if (defined $new_value) {
        $self->{_current_name} = $new_value;
    }
    else {
        $self->{_current_name} = $self->name unless defined $self->{_current_name};
    }
    return $self->{_current_name};
}

sub _init {
    my $self = shift;
    my $props = { @_ == 1 ? %{$_[0]} : @_ };

    $self->{'_linebreak'} = "\n";
    $self->{'_depth'} = 0;
    $self->{'_position'} = 0; # depth is for indentation only!
    $self->{'_array_padding'} = 0;
    $self->{'_seen'} = {};
    $self->{_refcount_base} = 3;
    $self->{'warnings'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'warning', 1);
    $self->{'indent'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'indent', 4);
    $self->{'index'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'index', 1);
    $self->{'name'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'name', 'var');
    $self->{'arrows'} = Data::Printer::Common::_fetch_anyof(
        $props,
        'arrows',
        'none',
        [qw(none first all)]
    );

    $self->{'show_tainted'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'show_tainted', 1);
    $self->{'show_tied'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'show_tied', 1);
    $self->{'show_weak'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'show_weak', 1);
    $self->{'show_unicode'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'show_unicode', 0);
    $self->{'show_readonly'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'show_readonly', 1);
    $self->{'show_lvalue'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'show_lvalue', 1);
    $self->{'show_refcount'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'show_refcount', 0);
    $self->{'show_memsize'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'show_memsize', 0);
    $self->{'memsize_unit'} = Data::Printer::Common::_fetch_anyof(
                                $props,
                                'memsize_unit',
                                'auto',
                                [qw(auto b k m)]
                            );
    $self->{'print_escapes'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'print_escapes', 0);
    $self->{'scalar_quotes'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'scalar_quotes', q("));
    $self->{'escape_chars'} = Data::Printer::Common::_fetch_anyof(
                            $props,
                            'escape_chars',
                            'none',
                            [qw(none nonascii nonlatin1 all)]
                        );
    $self->{'caller_info'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'caller_info', 0);
    $self->{'caller_message'} = Data::Printer::Common::_fetch_scalar_or_default(
                            $props,
                            'caller_message',
                            'Printing in line __LINE__ of __FILENAME__:'
                        );
    $self->{'caller_message_newline'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'caller_message_newline', 1);
    $self->{'caller_message_position'} = Data::Printer::Common::_fetch_anyof($props, 'caller_message_position', 'before', [qw(before after)]);
    $self->{'resolve_scalar_refs'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'resolve_scalar_refs', 0);
    $self->{'string_max'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'string_max', 4096);
    $self->{'string_preserve'} = Data::Printer::Common::_fetch_anyof(
                             $props,
                             'string_preserve',
                             'begin',
                             [qw(begin end middle extremes none)]
                         );
    $self->{'string_overflow'} = Data::Printer::Common::_fetch_scalar_or_default(
                                $props,
                                'string_overflow',
                                '(...skipping __SKIPPED__ chars...)'
                            );
    $self->{'array_max'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'array_max', 100);
    $self->{'array_preserve'} = Data::Printer::Common::_fetch_anyof(
                             $props,
                             'array_preserve',
                             'begin',
                             [qw(begin end middle extremes none)]
                         );
    $self->{'array_overflow'} = Data::Printer::Common::_fetch_scalar_or_default(
                                $props,
                                'array_overflow',
                                '(...skipping __SKIPPED__ items...)'
                        );
    $self->{'hash_max'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'hash_max', 100);
    $self->{'hash_preserve'} = Data::Printer::Common::_fetch_anyof(
                             $props,
                             'hash_preserve',
                             'begin',
                             [qw(begin end middle extremes none)]
                       );
    $self->{'hash_overflow'} = Data::Printer::Common::_fetch_scalar_or_default(
                                $props,
                                'hash_overflow',
                                '(...skipping __SKIPPED__ keys...)'
                       );
    $self->{'unicode_charnames'} = Data::Printer::Common::_fetch_scalar_or_default(
                               $props,
                               'unicode_charnames',
                               0
                           );
    $self->{'colored'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'colored', 'auto');
    $self->{'max_depth'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'max_depth', 0);
    $self->{'separator'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'separator', ',');
    $self->{'end_separator'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'end_separator', 0);
    $self->{'class_method'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'class_method', '_data_printer');
    $self->{'class'} = Data::Printer::Object::ClassOptions->new($props->{'class'});
    $self->{'hash_separator'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'hash_separator', '   ');
    $self->{'align_hash'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'align_hash', 1);
    $self->{'sort_keys'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'sort_keys', 1);
    $self->{'quote_keys'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'quote_keys', 'auto');
    $self->{'deparse'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'deparse', 0);
    $self->{'coderef_stub'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'coderef_stub', 'sub { ... }');
    $self->{'coderef_undefined'} = Data::Printer::Common::_fetch_scalar_or_default($props, 'coderef_undefined', '<undefined coderef>');
    $self->{'return_value'} = Data::Printer::Common::_fetch_anyof(
                             $props,
                             'return_value',
                             'pass',
                             [qw(pass dump void)]
                       );
    $self->{'show_dualvar'} = Data::Printer::Common::_fetch_anyof(
        $props,
        'show_dualvar',
        'lax',
        [qw(lax strict off)]
    );

    if (exists $props->{as}) {
        my $msg = Data::Printer::Common::_fetch_scalar_or_default($props, 'as', '');
        $self->{caller_info} = 1;
        $self->{caller_message} = $msg;
    }

    $self->multiline(
        Data::Printer::Common::_fetch_scalar_or_default($props, 'multiline', 1)
    );

    $self->fulldump(
        Data::Printer::Common::_fetch_scalar_or_default($props, 'fulldump', 0)
    );

    $self->output(defined $props->{output} ? $props->{output} : 'stderr');
    $self->_load_colors($props);
    $self->_load_filters($props);

    my %extra_config;
    my %core_options = map { $_ => 1 }
        (@method_names, qw(as multiline output colors filters));
    foreach my $key (keys %$props) {
        $extra_config{$key} = $props->{$key} unless exists $core_options{$key};
    }
    $self->{extra_config} = \%extra_config;

    return $self;
}

sub output {
    my ($self, $new_output) = @_;
    if (@_ > 1) {
        $self->_load_output_handle($new_output);
    }
    return $self->{output};
}

sub _load_output_handle {
    my ($self, $output) = @_;
    my %targets = ( stdout => *STDOUT, stderr => *STDERR );
    my $error;
    my $ref = ref $output;
    if (!$ref and exists $targets{ lc $output }) {
        $self->{output} = lc $output;
        $self->{output_handle} = $targets{ $self->{output} };
    }
    elsif ( ( $ref and $ref eq 'GLOB')
         or (!$ref and \$output =~ /GLOB\([^()]+\)$/)
    ) {
        $self->{output} = 'handle';
        $self->{output_handle} = $output;
    }
    elsif (!$ref or $ref eq 'SCALAR') {
        if (open my $fh, '>>', $output) {
            $self->{output} = 'file';
            $self->{output_handle} = $fh;
        }
        else {
            $error = "file '$output': $!";
        }
    }
    else {
        $error = 'unknown output data';
    }
    if ($error) {
        Data::Printer::Common::_warn($self, "error opening custom output handle: $error");
        $self->{output_handle} = $targets{'stderr'}
    }
    return;
}

sub new {
    my $class = shift;
    my $self = bless {}, $class;
    return $self->_init(@_);
}

sub multiline {
    my ($self, $value) = @_;
    if (defined $value) {
        $self->{multiline} = !!$value;
        if ($value) {
            $self->{_linebreak} = "\n";
            $self->{_current_indent} = $self->{indent};
            $self->index( $self->{_original_index} )
                if exists $self->{_original_index};
            $self->hash_separator( $self->{_original_separator} )
                if exists $self->{_original_separator};
            $self->array_overflow( $self->{_original_array_overflow} )
                if exists $self->{_original_array_overflow};
            $self->hash_overflow( $self->{_original_hash_overflow} )
                if exists $self->{_original_hash_overflow};
            $self->string_overflow( $self->{_original_string_overflow} )
                if exists $self->{_original_string_overflow};
        }
        else {
            $self->{_original_index} = $self->index;
            $self->index(0);
            $self->{_original_separator} = $self->hash_separator;
            $self->hash_separator(':');
            $self->{_original_array_overflow} = $self->array_overflow;
            $self->array_overflow('(...)');
            $self->{_original_hash_overflow} = $self->hash_overflow;
            $self->hash_overflow('(...)');
            $self->{_original_string_overflow} = $self->string_overflow;
            $self->string_overflow('(...)');
            $self->{_linebreak} = ' ';
            $self->{_current_indent} = 0;
        }
    }
    return $self->{multiline};
}

sub fulldump {
    my ($self, $value) = @_;
    if (defined $value) {
        $self->{fulldump} = !!$value;
        if ($value) {
            $self->{_original_string_max} = $self->string_max;
            $self->string_max(0);
            $self->{_original_array_max} = $self->array_max;
            $self->array_max(0);
            $self->{_original_hash_max} = $self->hash_max;
            $self->hash_max(0);
        }
        else {
            $self->string_max($self->{_original_string_max})
                if exists $self->{_original_string_max};
            $self->array_max($self->{_original_array_max})
                if exists $self->{_original_array_max};
            $self->hash_max($self->{_original_hash_max})
                if exists $self->{_original_hash_max};
        }
    }
}

sub _load_filters {
    my ($self, $props) = @_;

    # load our core filters (LVALUE is under the 'SCALAR' filter module)
    my @core_filters = qw(SCALAR ARRAY HASH REF VSTRING GLOB FORMAT Regexp CODE GenericClass);
    foreach my $class (@core_filters) {
        $self->_load_external_filter($class);
    }
    my @filters;
    # load any custom filters provided by the user
    if (exists $props->{filters}) {
        if (ref $props->{filters} eq 'HASH') {
            Data::Printer::Common::_warn(
                $self,
                'please update your code: filters => { ... } is now filters => [{ ... }]'
            );
            push @filters, $props->{filters};
        }
        elsif (ref $props->{filters} eq 'ARRAY') {
            @filters = @{ $props->{filters} };
        }
        else {
            Data::Printer::Common::_warn($self, 'filters must be an ARRAY reference');
        }
    }
    foreach my $filter (@filters) {
        my $filter_reftype = Scalar::Util::reftype($filter);
        if (!defined $filter_reftype) {
            $self->_load_external_filter($filter);
        }
        elsif ($filter_reftype eq 'HASH') {
            foreach my $k (keys %$filter) {
                if ($k eq '-external') {
                    Data::Printer::Common::_warn(
                        $self,
                        'please update your code: '
                      . 'filters => { -external => [qw(Foo Bar)}'
                      . ' is now filters => [qw(Foo Bar)]'
                    );
                    next;
                }
                if (Scalar::Util::reftype($filter->{$k}) eq 'CODE') {
                    my $type = Data::Printer::Common::_filter_category_for($k);
                    unshift @{ $self->{$type}{$k} }, $filter->{$k};
                }
                else {
                    Data::Printer::Common::_warn(
                        $self,
                        'hash filters must point to a CODE reference'
                    );
                }
            }
        }
        else {
            Data::Printer::Common::_warn($self, 'filters must be a name or { type => sub {...} }');
        }
    }
    return;
}

sub _load_external_filter {
    my ($self, $class) = @_;
    my $module = "Data::Printer::Filter::$class";
    my $error = Data::Printer::Common::_tryme("use $module; 1;");
    if ($error) {
        Data::Printer::Common::_warn($self, "error loading filter '$class': $error");
        return;
    }
    my $from_module = $module->_filter_list;
    foreach my $kind (keys %$from_module) {
        foreach my $name (keys %{$from_module->{$kind}}) {
            unshift @{ $self->{$kind}{$name} }, @{ $from_module->{$kind}{$name} };
        }
    }
    return;
}

sub _detect_color_level {
    my ($self) = @_;
    my $colored = $self->colored;
    my $color_level;

    # first we honour ANSI_COLORS_DISABLED, colored and writing to files
    if (   !$colored
        || ($colored eq 'auto'
            && (exists $ENV{ANSI_COLORS_DISABLED}
                || $self->output eq 'handle'
                || $self->output eq 'file'
            )
        )
    ) {
        $color_level = 0;
    }
    else {
        # NOTE: we could try `tput colors` but it may not give
        # the proper result, so instead we do what most terminals
        # currently do and rely on environment variables.
        if ($ENV{COLORTERM} && $ENV{COLORTERM} eq 'truecolor') {
            $color_level = 3;
        }
        elsif ($ENV{TERM_PROGRAM} && $ENV{TERM_PROGRAM} eq 'iTerm.app') {
            my $major_version = substr($ENV{TERM_PROGRAM_VERSION} || '0', 0, 1);
            $color_level = $major_version >= 3 ? 3 : 2;
        }
        elsif ($ENV{TERM_PROGRAM} && $ENV{TERM_PROGRAM} eq 'Apple_Terminal') {
            $color_level= 2;
        }
        elsif ($ENV{TERM} && $ENV{TERM} =~ /\-256(?:color)?\z/i) {
            $color_level = 2;
        }
        elsif ($ENV{TERM}
            && ($ENV{TERM} =~ /\A(?:screen|xterm|vt100|rxvt)/i
                || $ENV{TERM} =~ /color|ansi|cygwin|linux/i)
        ) {
            $color_level = 1;
        }
        elsif ($ENV{COLORTERM}) {
            $color_level = 1;
        }
        else {
            $color_level = $colored eq 'auto' ? 0 : 1;
        }
    }
    return $color_level;
}

sub _load_colors {
    my ($self, $props) = @_;

    $self->{_output_color_level} = $self->_detect_color_level;

    my $theme_object;
    my $default_theme = 'Material';
    my $theme_name    = Data::Printer::Common::_fetch_scalar_or_default($props, 'theme', $default_theme);
    $theme_object = Data::Printer::Theme->new(
        name            => $theme_name,
        color_overrides => $props->{colors},
        color_level     => $self->{_output_color_level},
        ddp             => $self,
    );
    if (!$theme_object) {
        if ($theme_name ne $default_theme) {
            $theme_object = Data::Printer::Theme->new(
                name            => $default_theme,
                color_overrides => $props->{colors},
                color_level     => $self->{_output_color_level},
                ddp             => $self,
            );
        }
        Data::Printer::Common::_die("Unable to load default theme. This should never happen - please contact the author") unless $theme_object;
    }
    $self->{theme} = $theme_object;
}

sub _filters_for_type {
    my ($self, $type) = @_;
    return exists $self->{type_filters}{$type} ? @{ $self->{type_filters}{$type} } : ();
}

sub _filters_for_class {
    my ($self, $type) = @_;
    return exists $self->{class_filters}{$type} ? @{ $self->{class_filters}{$type} } : ();
}

sub _filters_for_data {
    my ($self, $data) = @_;

    # we favour reftype() over ref() because you could have
    # a HASH.pm (or ARRAY.pm or whatever) blessing any variable.
    my $ref_kind = Scalar::Util::reftype($data);
    $ref_kind = 'SCALAR' unless $ref_kind;

    # ref() returns 'Regexp' but reftype() returns 'REGEXP', so we picked one:
    $ref_kind = 'Regexp' if $ref_kind eq 'REGEXP';

    my @potential_filters;

    # first, try class name + full inheritance for a specific name.
    my $class = Scalar::Util::blessed($data);

    # a regular regexp is blessed, but in that case we want a
    # regexp filter, not a class filter.
    if (defined $class && $class eq 'Regexp') {
        if ($ref_kind eq 'Regexp' || ($] < 5.011 && $ref_kind eq 'SCALAR')) {
            $ref_kind = 'Regexp';
            undef $class;
        }
    }
    if (defined $class) {
        if ($self->class->parent_filters) {
            my $linear_ISA = Data::Printer::Common::_linear_ISA_for($class, $self);
            foreach my $candidate_class (@$linear_ISA) {
                push @potential_filters, $self->_filters_for_class($candidate_class);
            }
        }
        else {
            push @potential_filters, $self->_filters_for_class($class);
        }
        # next, let any '-class' filters have a go:
        push @potential_filters, $self->_filters_for_class('-class');
    }

    # then, try regular data filters
    push @potential_filters, $self->_filters_for_type($ref_kind);

    # finally, if it's neither a class nor a known core type,
    # we must be in a future perl with some type we're unaware of:
    push @potential_filters, $self->_filters_for_class('-unknown');

    return @potential_filters;
}

# _see($data): marks data as seen if it was never seen it before.
# if we are showing refcounts, we return those. Initially we had
# this funcionallity separated, but refcounts increase as we find
# them again and because of that we were seeing weird refcounting.
# So now instead we store the refcount of the variable when we
# first see it.
# Finally, if we have already seen the data, we return its stringified
# position, like "var", "var{foo}[7]", etc. UNLESS $options{seen_override}
# is set. Why seen_override? Sometimes we want to print the same data
# twice, like the GenericClass filter, which prints the object's metadata
# via parse() and then the internal structure via parse_as(). But if we
# simply do that, we'd get the "seen" version (because we have already
# visited it!) The refcount is still calculated only once though :)
sub _see {
    my ($self, $data, %options) = @_;
    return {} unless ref $data;
    my $id = pack 'J', Scalar::Util::refaddr($data);
    if (!exists $self->{_seen}{$id}) {
        $self->{_seen}{$id} = {
            name     => $self->current_name,
            refcount => ($self->show_refcount ? $self->_refcount($data) : 0),
        };
        return { refcount => $self->{_seen}{$id}->{refcount} };
    }
    return { refcount => $self->{_seen}{$id}->{refcount} } if $options{seen_override};
    return $self->{_seen}{$id};
}

sub seen {
    my ($self, $data) = @_;
    my $id = pack 'J', Scalar::Util::refaddr($data);
    return exists $self->{_seen}{$id};
}

sub unsee {
    my ($self, $data) = @_;
    return unless ref $data && keys %{$self->{_seen}};

    my $id = pack 'J', Scalar::Util::refaddr($data);
    delete $self->{_seen}{$id};
    return;
}

sub _refcount {
    my ($self, $data) = @_;

    require B;
    my $count;
    my $rv = B::svref_2object(\$data)->RV;
    if (ref($data) eq 'REF' && ref($$data)) {
        $rv = B::svref_2object($data)->RV;
    }

    # some SV's are special (represented by B::SPECIAL)
    # and don't have a ->REFCNT (e.g. \undef)
    return 0 unless $rv->can( 'REFCNT' );

    # 3 is our magical number: so we return the actual reference count
    # minus the references we added as we were traversing:
    return $rv->REFCNT - $self->{_refcount_base};
}

sub parse_as {
    my ($self, $type, $data) = @_;
    return $self->parse($data, force_type => $type, seen_override => 1);
}

# parse() must always receive a reference, never a regular copy, because
# that's the only way we are able to figure whether the source data
# is a weak ref or not.
sub parse {
    my $self = shift;
    my $str_weak = $self->_check_weak( $_[0] );

    my ($data, %options) = @_;
    my $parsed_string = '';

    # if we've seen this structure before, we return its location
    # instead of going through it again. This avoids infinite loops
    # when parsing circular references:
    my $seen = $self->_see($data, %options);
    if (my $name = $seen->{name}) {
        $parsed_string .= $self->maybe_colorize(
            ((ref $data eq 'SCALAR' && $self->resolve_scalar_refs)
                ? $$data
                : $name
            ),
            'repeated'
        );
        # on repeated references, the only extra data we put
        # is whether this reference is weak or not.
        $parsed_string .= $str_weak;
        return $parsed_string;
    }
    $self->{_position}++;

    # Each filter type provides an array of potential parsers.
    # Once we find the right kind, we go through all of them,
    # from most precise match to most generic.
    # The first filter that returns a defined value "wins"
    # (even if it's an empty string)
    foreach my $filter (
        exists $options{force_type}
          ? $self->_filters_for_type($options{force_type})
          : $self->_filters_for_data($data)
    ) {
        if (defined (my $result = $filter->($data, $self))) {
            $parsed_string .= $result;
            last;
        }
    }

    # FIXME: because of prototypes, p(@data) becomes a ref (that we don't care about)
    # to the data (that we do care about). So we should not show refcounts, memsize
    # or readonly status for something guaranteed to be ephemeral.
    $parsed_string .= $self->_check_readonly($data);
    $parsed_string .= $str_weak if ref($data) ne 'REF';
    $parsed_string .= $self->_check_memsize($data);

    if ($self->show_refcount && ref($data) ne 'SCALAR' && $seen->{refcount} > 1 ) {
        $parsed_string .= ' (refcount: ' . $seen->{refcount} .')';
    }

    if (--$self->{'_position'} == 0) {
        $self->{'_seen'} = {};
        $self->{'_refcount_base'} = 3;
        $self->{'_position'} = 0;
    }

    return $parsed_string;
}

sub _check_memsize {
    my ($self, $data) = @_;
    return '' unless $self->show_memsize
                  && (   $self->show_memsize eq 'all'
                      || $self->show_memsize >= $self->{_position});
    my $size;
    my $unit;
    my $error = Data::Printer::Common::_tryme(sub {
        require Devel::Size;
        $size = Devel::Size::total_size($data);
        $unit = uc $self->memsize_unit;
        if ($unit eq 'M' || ($unit eq 'AUTO' && $size > 1024*1024)) {
            $size = $size / (1024*1024);
            $unit = 'M';
        }
        elsif ($unit eq 'K' || ($unit eq 'AUTO' && $size > 1024)) {
            $size = $size / 1024;
            $unit = 'K';
        }
        else {
            $unit = 'B';
        }
    });
    if ($error) {
        if ($error =~ m{locate Devel/Size.pm}) {
            Data::Printer::Common::_warn($self, "Devel::Size not found, show_memsize will be ignored")
                if $self->{_position} == 1;
        }
        else {
            Data::Printer::Common::_warn($self, "error fetching memory usage: $error");
        }
        return '';
    }
    return '' unless $size;
    my $string = ' (' . ($size < 0 ? sprintf("%.2f", $size) : int($size)) . $unit . ')';
    return $self->maybe_colorize($string, 'memsize');
}

sub _check_weak {
    my ($self) = shift;
    return '' unless $self->show_weak;

    my $realtype = Scalar::Util::reftype($_[0]);
    my $isweak;
    if ($realtype && ($realtype eq 'REF' || $realtype eq 'SCALAR')) {
        $isweak = Scalar::Util::isweak($_[0]);
    }
    else {
        $isweak = Scalar::Util::isweak($_[0]);
    }
    return '' unless $isweak;
    return ' ' . $self->maybe_colorize('(weak)', 'weak');
}

sub _write_label {
    my ($self) = @_;
    return '' unless $self->caller_info;
    my @caller = caller 1;

    my $message = $self->caller_message;

    $message =~ s/\b__PACKAGE__\b/$caller[0]/g;
    $message =~ s/\b__FILENAME__\b/$caller[1]/g;
    $message =~ s/\b__LINE__\b/$caller[2]/g;

    my $separator = $self->caller_message_newline ? "\n" : ' ';
    $message = $self->maybe_colorize($message, 'caller_info');
    $message = $self->caller_message_position eq 'before'
        ? $message . $separator
        : $separator . $message
        ;
    return $message;
}

sub maybe_colorize {
    my ($self, $output, $color_type, $default_color, $end_color) = @_;

    if ($self->{_output_color_level} && defined $color_type) {
        my $theme = $self->theme;
        my $sgr_color = $theme->sgr_color_for($color_type);
        if (!defined $sgr_color && defined $default_color) {
            $sgr_color = $theme->_parse_color($default_color);
        }
        if ($sgr_color) {
            $output = $sgr_color
                . $output
                . (defined $end_color
                    ? $theme->sgr_color_for($end_color)
                    : $theme->color_reset
                );
        }
    }
    return $output;
}

sub _check_readonly {
    my ($self) = @_;
    return ' (read-only)' if $self->show_readonly && &Internals::SvREADONLY($_[1]);
    return '';
}

42;
__END__

=head1 NAME

Data::Printer::Object - underlying object for Data::Printer

=head1 SYNOPSIS

Unless you're writing a plugin, or looking for some
L<< configuration property details|/Attributes >>
the documentation you want is probably on L<Data::Printer>. Seriously!


=head1 DESCRIPTION

This module implements the underlying object used by Data::Printer to parse,
format and print Perl data structures.

It is passed to plugins so they can rely on contextual information from the
caller like colors, spacing and other options.

=head1 COMMON PROPERTIES / ATTRIBUTES

=head2 Scalar Options

=head3 show_tainted

When set, will detect and let you know of any tainted data (default: 1)
Note that this is a no-op unless your script is in taint mode, meaning
it's running with different real and effective user/group IDs, or with the
-T flag. See L<perlsec> for extra information.

=head3 show_unicode

Whether to label data that has the L<unicode flag|perlunifaq> set. (default: 1)

=head3 show_dualvar

Perl can interpret strings as numbers and vice-versa, but that doesn't mean
it always gets it right. When this option is set to "lax", Data::Printer will
show both values if they differ. If set to "strict", it will always show both
values, and when set to "off" it will never show the second value. (default: lax)

=head3 show_lvalue

Let's you know whenever a value is an lvalue (default: 1)

=head3 string_max

The maximum number of characters to display in a string. If the string is
bigger than that, Data::Printer will trim a part of the string (set by
L<string_preserve|/string_preserve>) and replace it with the message set on
L<string_overflow|/string_overflow>. Set C<string_max> to 0 to show all
characters (default: 4096)

=head3 string_overflow

Message to display once L<string_max|/string_max> is reached. Defaults to
I<< "(...skipping __SKIPPED__ chars...)" >>.

=head3 string_preserve

When the string has more characters than L<string_max|/string_max>, this
option defines which part of the string to preserve. Can be set to 'begin',
'middle' or 'end'. (default: 'begin')

=head3 scalar_quotes

Which quotation character to use when printing strings (default: ")

=head3 escape_chars

Use this to escape certain characters from strings, which could be useful if
your terminal is in a different encoding than the data being printed. Can be
set to 'nonascii', 'nonlatin1', 'all' or 'none' (default: none).

=head3 unicode_charnames

whether to use the character's names when escaping unicode (e.g. SNOWMAN instead of \x{2603}) (default: 0)

=head3 print_escapes

Wether to print invisible characters in strings, like \b, \n and \t (default: 0)

=head3 resolve_scalar_refs

If a reference to a scalar value is found more than once, print the resolved
value. For example, you may have an object that you reuse to represent 'true'
or 'false'. If you have more than one of those in your data, Data::Printer
will by default print the second one as a circular reference. When this option
is set to true, it will instead resolve the scalar value and keep going. (default: false)


=head2 Array Options

=head3 array_max

The maximum number of array elements to show. If the array is bigger than
that, Data::Printer will trim the offending slice (set by
L<array_preserve|/array_preserve>) and replace it with the message set on
L<array_overflow|/array_overflow>. Set C<array_max> to 0 to show all elements
in the array, regardless of array size (default: 100)

=head3 array_overflow

Message to display once L<array_max|/array_max> is reached. Defaults to
C<< "(...skipping __SKIPPED__ items...)" >>.

=head3 array_preserve

When an array has more elements than L<array_max|/array_max>, this option
defines which part of the array to preserve. Can be set to 'begin', 'middle'
or 'end'. (default: 'begin')

=head3 index

When set, shows the index number before each array element. (default: 1)


=head4 Hash Options

=head3 align_hash

If this option is set, hash keys  will be vertically aligned by the length
of the longest key.

This is better explained with an example, so consider the hash
C<< my %h = ( a => 123, aaaaaa => 456 ) >>. This would be an unaligned output:

    a => 123,
    aaaaaa => 456

and this is what it looks like with C<< align_hash = 1 >>:

    a      => 123,
    aaaaaa => 456

(default: 1)

=head3 hash_max

The maximum number of hash key/value pairs to show. If the hash is bigger than
that, Data::Printer will trim the offending slice (set by
L<hash_preserve|/hash_preserve>) and replace it with the message set on
L<hash_overflow|/hash_overflow>. Set C<hash_max> to 0 to show all elements
in the hash, regardless of the total keys. (default: 100)

=head3 hash_overflow

Message to display once L<hash_max|/hash_max> is reached. Defaults to
C<< "(...skipping __SKIPPED__ keys...)" >>.

=head3 hash_preserve

When a hash has more elements than L<hash_max|/hash_max>, this option
defines which part of the hash to preserve. Can be set to 'begin', 'middle'
or 'end'. Note that Perl makes no promises regarding key order, so this
option only makes sense if keys are sorted. In other words, if
you have disabled L<sort_keys|/sort_keys>, expect random keys to be
shown regardless of which part was preserved. (default: 'begin')

=head3 hash_separator

What to use to separate keys from values. Default is '   ' (three spaces)

=head3 sort_keys

Whether to sort keys when printing the contents of a hash (default: 1)

=head3 quote_keys

Whether to quote hash keys or not. Can be set to 1 (always quote), 0
(never quote) or 'auto' to quote only when a key contains spaces or
linebreaks. (default: 'auto')


=head2 Caller Information

Data::Printer can add an informational message to every call to C<p()> or
C<np()> if you enable C<caller_info>. So for example if you write:

    my $var = "meep!";
    p $var, caller_info => 1;

this will output something like:

    Printing in line 2 of myapp.pl:
    "meep!"

The following options let you customize the message and how it is displayed.

=head3 caller_info

Set this option to a true value to display a L<message|/caller_message> next
to the data being printed. (default: 0)

=head3 caller_message

What message to print when L<caller_info|/caller_info> is true.

Defaults to
"C<< Printing in line __LINE__ of __FILENAME__ >>".

If the special strings C<__LINE__>, C<__FILENAME__> or C<__PACKAGE__> are
present in the message, they'll be interpolated into their according value
so you can customize the message at will:

    caller_message = "[__PACKAGE__:__LINE__]"

=head3 caller_message_newline

When true, skips a line when printing L<caller_message|/caller_message>.
When false, only a single space is added between the message and the data.
(default: 1)

=head3 caller_message_position

This option controls where the L<caller_message|/caller_message> will appear
in relation to the code being printed. Can be set to 'before' or 'after'. A
line is always skipped between the message and the data (either before or
after), unless you set L<caller_message_newline|/caller_message_newline> to 0.
(default: 'before')


=head2 General Options

=head3 arrows

Data::Printer shows circular references as a data path, indicating where in
the data that reference points to. You may use this option to control if/when
should it print reference arrows. Possible values are 'all' (e.g
C<< var->{x}->[y]->[z] >>), 'first' (C<< var->{x}[y][z] >>) or 'none'
(C<< var{x}[y][z] >>). Default is 'none'.

=head3 colored

Whether to colorize the output or not. Can be set to 1 (always colorize), 0
(never colorize) or 'auto'. Default is 'auto', meaning it will colorize only
when printing to STDOUT or STDERR, never to a file or to a variable. The 'auto'
setting also respects the C<ANSI_COLORS_DISABLED> environment variable.

=head3 deparse

If the data structure contains a subroutine reference (coderef), this option
can be set to deparse it and print the underlying code, which hopefully
resembles the original source code. (default: 0)

=head3 coderef_stub

If the data structure contains a subroutine reference (coderef) and the
'L<deparse|/deparse>' option above is set to false, Data::Printer will print this
instead. (default: 'C<< sub { ... } >>')

=head3 coderef_undefined

If the data structure contains a subroutine reference (coderef) that has
not actually been defined at the time of inspection, Data::Printer will
print this instead. Set it to '0' to disable this check, in which case
Data::Printer will use whatever value you set on
L<coderef_stub|/coderef_stub> above. (default: '<undefined coderef>').

=head3 end_separator

When set, the last item on an array or hash will always contain a
trailing L<separator|/separator>. (default: 0)

=head3 show_memsize

Set to true and Data::Printer will show the estimate memory size of the data
structure being printed. Requires Devel::Size. (default: 0)

=head3 memsize_unit

If L<show_memsize|/show_memsize> is on, this option lets you specify the
unit in which to show the memory size. Can be set to "b" to show size in
bytes, "k" for kilobytes, "m" for megabytes or "auto", which will use the
biggest unit that makes sense. (default: auto)

=head3 output

Where you want the output to be printed. Can be set to the following values:

=over 4

=item * C<'stderr'> - outputs to the standard error handle.

=item * C<'stdout'> - outputs to the standard output handle.

=item * reference to a scalar (e.g. C<\$string>) - outputs to the scalar reference.

=item * file handle - any open file handle:

    open my $fh, '>>', '/path/to/some/file.log' or die $!;
    p @{[ 1,2,3 ]}, output => $fh;

=item * file path - if you pass a non-empty string that is not 'stderr' nor 'stdout',
Data::Printer will consider it to be a file path and create/append to it automatically
for you. So you can do this in your C<.dataprinter>:

    output = /path/to/some/file.log

By default, Data::Printer will print to the standard error (stderr).

=back

=head3 max_depth

This setting controls how far inside the data structure we should go
(default: 0 for no depth limit)

=head3 return_value

Whether the user wants the return value to be a pass-through of the source
data ('pass'), the dump content itself ('dump') or nothing at all ('void').

Defaults to C<'pass'> since version 0.36. B<NOTE>: if you set it to 'dump',
make sure it's not the last statement of a subroutine or that, if it is, the
sub is only called in void context.

=head3 separator

The separator character(s) to use for arrays and hashes. The default is the
comma ",".

=head3 show_readonly

When this option is set, Data::Printer will let you know whenever a value is
read-only. (default: 1)

=head3 show_refcount

Whether to show data refcount it's above 1 (default: 0)

=head3 show_weak

When this option is set, Data::Printer will let you know whenever it finds a
weak reference (default: 1)

=head3 show_tied

When set to true, this option will let you know whenever a tied variable
is detected, including what is tied to it (default: 1)

=head3 theme

    theme = Monokai

This setting gets/sets the current color theme module. The default theme
is L<Material|Data::Printer::Theme::Material>. Data::Printer ships with
several themes for you to choose, and you can create your own theme or use
any other from CPAN.

=head3 warnings

If something goes wrong when parsing your data or printing it to the selected
output, Data::Printer by default shows you a warning from the standpoint of
the actual call to C<p()> or C<np()>. To silence those warnings, set this
option to 0.


=head2 Class / Object Options

=head3 class_method

When Data::Printer is printing an object, it first looks for a method
named "C<_dataprinter>" and, if one is found, we call it instead of actually
parsing the structure.

This way, module authors can control how Data::Printer outputs their objects
the best possible way by simply adding a private method instead of having
to write a full filter or even adding Data::Printer as a dependency.

To disable this behavior, simply set this option to false or an empty string.
You can also change it to a different name and Data::Printer will look for
that instead.

=head3 class - class properties to override.

This "namespace" gets/sets all class properties that are used by the
L<standard class filter|Data::Printer::Filter::GenericClass> that ships
with Data::Printer. Note that, if you are using a specific filter for that
object, most (if not all) of the settings below will not apply.

In your C<.dataprinter> file, the defaults would look like this:

    class.parents            = 1
    class.linear_isa         = auto
    class.universal          = 0
    class.expand             = 1
    class.stringify          = 1
    class.show_reftype       = 0
    class.show_overloads     = 1
    class.show_methods       = all
    class.sort_methods       = 1
    class.inherited          = public
    class.format_inheritance = lines
    class.parent_filters     = 1
    class.internals          = 1

In code, you should use the "class" namespace as a key to a hash reference:

    use Data::Printer class => {
        parents            => 1,
        linear_isa         => 'auto',
        universal          => 0,
        expand             => 1,
        stringify          => 1,
        show_reftype       => 0,
        show_overloads     => 1,
        show_methods       => 'all',
        sort_methods       => 1,
        inherited          => 'public',
        format_inheritance => 'lines',
        parent_filters     => 1,
        internals          => 1,
    };

Or inline:

    p $some_object, class => { internals => 1,  ... };


=head4 parents

When set, shows all superclasses of the object being printed. (default: 1)

=head4 linear_isa

This setting controls whether to show the linearized @ISA, which is the
order of preference in which the object's methods and attributes are resolved
according to its inheritance. Can be set to 1 (always show), 0 (never show)
or 'auto', which shows only when the object has more than one superclass.
(default: 'auto')

=head4 universal

Set this option to 1 to include UNIVERSAL methods to the list of public
methods (like C<can> and C<isa>). (default: 0)

=head4 expand

Sets how many levels to descend when printing classes, in case their internals
point to other classes. Set this to 0 to never expand any objects, just show
their name. Set to any integer number and when Data::Printer reaches that
depth, only the class name will be printed. Set to 'all' to always expand
objects found inside your object. (default: 1)

=head4 stringify

When this option is set, Data::Printer will check if the object being printed
contains any methods named C<as_string>, C<to_string> or C<stringify>. If it
does, Data::Printer will use it as the object's output instead of the
generic class plugin. (default: 1)

=head4 show_reftype

If set to a true value, Data::Printer will show the internal reference type
of the object. (default: 0)

=head4 show_overloads

This option includes a list of all overloads implemented by the object.
(default: 1)

=head4 show_methods

Controls which of the object's direct methods to show. Can be set to 'none',
'all', 'private' or 'public'. When applicable (Moo, Moose) it will also
show attributes and roles. (default: 'all')

=head4 sort_methods

When listing methods, attributes and roles, this option will order them
alphabetically, rather than on whatever order the list of methods returned.
(default: 1)

=head4 inherited

Controls which of the object's parent methods to show. Can be set to 'none',
'all', 'private' or 'public'. (default: 'public')

=head4 format_inheritance

This option controls how to format the list of methods set by a parent class
(and not the class itself). Setting it to C<'lines'> it will print one line
for each parent, like so:

    public methods (5):
        foo, bar
        Parent::Class:
            baz, meep
        Other::Parent:
            moop


Setting it to C<'string'>, it will put all methods on the same line:

    public methods (5): foo, bar, baz (Parent::Class), meep (Parent::CLass), moop (Other::Parent)

Default is: 'lines'.


=head4 parent_filters

If there is no filter for the given object's class, there may still be a
filter for one of its parent classes. When this option is set, Data::Printer
will traverse the object's superclass and use the first filter it finds,
if one is present. (default: 1)

=head4 internals

Shows the object's internal data structure. (default: 1)


=head2 "Shortcuts"

Some options are so often used together we have created shortcuts for them.

=head3 as

    p $somevar, as => 'is this right?';

The "C<as>" shortcut activates L<caller_info|/caller_info> and sets
L<caller_message|/caller_message> to whatever you set it to. It's really
useful to quickly differentiate between sequential uses of C<p()>.

=head3 multiline

    p $somevar, multiline => 0;

When set to 0, disables array index and linebreaks, uses ':' as hash separator
and '(...)' as overflow for hashes, arrays and strings, and also disables
'caller_message_newline' so any caller message is shown on the same line as
the variable being printed. If this is set on a global configuration or on the
C<.dataprinter> file, Can be "undone" by setting it to "1".

=head3 fulldump

    p $somevar, fulldump => 1;

By default, Data::Printer limits the size of string/array/hash dumps to a
(hopefully) reasonable size. Still, sometimes you really need to see
everything. To completely disable such limits, just set this option to true.


=head2 Methods and Accessors for Filter Writers

The following attributes could be useful if you're writing your own custom
filters or maybe even a non-obvious profile. Otherwise, no need to worry about
any of them ;)

And make sure to check out the current filter list for real usage examples!

=head3 indent

=head3 outdent

=head3 newline

These methods are used to control the indentation level of the string being
created to represent your data. While C<indent> and C<outdent> respectively
increase and decrease the indentation level, C<newline> will add a linebreak
and position the "cursor" where you are expected to continue your dump string:

    my $output = $ddp->newline . 'this is a new line';
    $ddp->indent;
    $output .= $ddp->newline . 'this is indented';
    $ddp->outdent;
    $output .= $ddp->newline . 'back to our previous indentation!';

Unless multiline was set to 0, the code above should print something like:

    this is a new line
        this is indented
    back to our previous indentation

=head3 extra_config

Data::Printer will read and pass-through any unrecognized settings in either
your C<.dataprinter> file or your inline arguments inside this structure.
This is useful to create custom settings for your filters.

While any and all unknown settings will be readable here, we recommend you
prepend them with a namespace like C<filter_xxx> as those are reserved for
filters and thus guaranteed not to colide with any core Data::Printer
settings now or in the future.

For example, on the L<Web filter|Data::Printer::Filter::Web> we have the
C<expand_headers> option, and even though Data::Printer itself doesn't have
this option, we prepend everything with the C<filter_web> namespace, either
in the config file:

    filter_web.expand_headers = 1

or inline:

    p $http_response, filters => ['Web'], filter_web => { expand_headers => 1 };


=head3 maybe_colorize( $string, $label )

=head3 maybe_colorize( $string, $label, $default_color )

    my $output = $ddp->maybe_colorize( 12.3, 'number');

Instead of simply adding raw content to your dump string, you should wrap it
with this method, as it will look up colors on the current theme and print
them (or not, depending on whether the terminal supports color or the user
has explicitly turned them off).

If you are writing a custom filter and don't want to use the core labels to
colorize your content, you may want to set your own label and pass a default
color. For example:

    my $output = $ddp->maybe_colorize( $data, 'filter_myclass', '#ffccb3' );

In the code above, if the user has C<colors.filter_myclass> set either on the
C<.dataprinter> file or the runtime hashref, that one will be used. Otherwise,
Data::Printer will use C<'#ffccb3'>.

=head3 current_depth

Shows the current depth level, from 0 onwards.

=head3 current_name

Gets/sets the name for the current posistion, to be printed when the parser
visits that data again. E.g. C<var[0]{abc}[2]>.

=head3 parse( $data_ref )

=head3 parse( $data_ref, %options )

This method receives a reference to a data structure to parse, and returns the
parsed string. It will call each filter and colorize the output accordingly.

Use this inside filters whenever you want to use the result of a parsed data
strucure.

    my $output = $ddp->parse( [3,2,1] );

An optional set of parameters may be passed:

=over 4

=item * C<< force_type => $type >> - forces data to be treated as that type,
where $type is the name of the Perl data strucuture as returned by
Scalar::Util::reftype (e.g. 'HASH', 'ARRAY' etc). This is used when a filter
wants to show the internals of blessed data. Otherwise parse would just call
the same filter over and over again.

=item * C<< seen_override => 1 >> - Data::Printer::Object tries to remember
if it has already seen a data structure before, so it can show the circular
reference instead of entenring an infinite loop. However, there are cases
when you want to print the same data structure twice, like when you're doing
a second pass on a blessed object to print its internals, or if you're using
the same object over and over again. This setting overrides the internal
counter and prints the same data again. Check L<unsee|/unsee( $data )> below
for another way to achieve this.

=back

=head3 parse_as( $type, $data_ref )

This is a convenience method to force some data to be interpreted as a
particular type. It is the same as:

    $ddp->parse( $data, force_type => $type, seen_override => 1 );

=head2 unsee( $data )

Sometimes you are writing a filter for data that you know will be repeated
several times, like JSON Boolean objects. To prevent Data::Printer from
showing this content as repeated, you can use the C<unsee> method to make
the current object forget about having ever visited this data.


=head1 OBJECT CONSTRUCTION

You'll most like never need this unless you're planning on extending
Data::Printer itself.

=head2 new( %options )

Creates a new Data::Printer::Object instance. It may (optionally) receive a
hash or hash reference with custom settings for any of its properties.


=head1 SEE ALSO

L<Data::Printer>