File Coverage

blib/lib/Treex/Core/Node.pm
Criterion Covered Total %
statement 290 446 65.0
branch 109 212 51.4
condition 40 92 43.4
subroutine 44 63 69.8
pod 35 41 85.3
total 518 854 60.6


line stmt bran cond sub pod time code
1             package Treex::Core::Node;
2             $Treex::Core::Node::VERSION = '2.20210102';
3 24     24   109349 use namespace::autoclean;
  24         19504  
  24         228  
4              
5 24     24   2597 use Moose;
  24         483368  
  24         170  
6 24     24   164960 use MooseX::NonMoose;
  24         2115  
  24         170  
7 24     24   245331 use Treex::Core::Common;
  24         70  
  24         217  
8 24     24   180857 use Cwd;
  24         70  
  24         2241  
9 24     24   221 use Scalar::Util qw(refaddr);
  24         69  
  24         1294  
10 24     24   16234 use Treex::PML;
  24         5692057  
  24         25234  
11              
12             extends 'Treex::PML::Node';
13             with 'Treex::Core::WildAttr';
14             with 'Treex::Core::Node::Aligned';
15              
16             # overloading does not work with namespace::autoclean
17             # see https://rt.cpan.org/Public/Bug/Display.html?id=50938
18             # We may want to use https://metacpan.org/module/namespace::sweep instead.
19             #
20             # use overload
21             # '""' => 'to_string',
22             # '==' => 'equals',
23             # '!=' => '_not_equals',
24             # 'eq' => 'equals', # deprecated
25             # 'ne' => '_not_equals', # deprecated
26             # 'bool' => sub{1},
27             #
28             # # We can A) let Magic Autogeneration to build "derived" overloadings,
29             # # or B) we can disable this feature (via fallback=>0)
30             # # and define only the needed overloadings
31             # # (so all other overloadings will result in fatal errors).
32             # # See perldoc overload.
33             # # I decided for A, but uncommenting the following lines can catch some misuses.
34             # #'!' => sub{0},
35             # #'.' => sub{$_[2] ? $_[1] . $_[0]->to_string : $_[0]->to_string . $_[1]},
36             # #fallback => 0,
37             # ;
38             # # TODO: '<' => 'precedes' (or better '<=>' => ...)
39             # # 'eq' => sub {log_warn 'You should use ==' && return $_[0]==$_[1]} # similarly for 'ne'
40              
41             Readonly my $_SWITCHES_REGEX => qr/^(ordered|add_self|(preceding|following|first|last)_only)$/x;
42             my $CHECK_FOR_CYCLES = 1;
43              
44             our $LOG_NEW = 0;
45             our $LOG_EDITS = 0;
46             # tip: you can use Util::Eval doc='$Treex::Core::Node::LOG_EDITS=1;' in your scenario
47             # Note that most attributes are not set by set_attr. See TODO below.
48              
49             has _zone => (
50             is => 'rw',
51             writer => '_set_zone',
52             reader => '_get_zone',
53             weak_ref => 1,
54             );
55              
56             has id => (
57             is => 'rw',
58             trigger => \&_index_my_id,
59             );
60              
61             sub BUILD {
62 261     261 0 583838 my ( $self, $arg_ref ) = @_;
63              
64 261 0 33     1702 if (( not defined $arg_ref or not defined $arg_ref->{_called_from_core_} )
      33        
65             and not $Treex::Core::Config::running_in_tred
66             )
67             {
68 0         0 log_fatal 'Because of node indexing, no nodes can be created outside of documents. '
69             . 'You have to use $zone->create_tree(...) or $node->create_child() '
70             . 'instead of Treex::Core::Node...->new().';
71             }
72 261         674 return;
73             }
74              
75             sub to_string {
76 0     0 1 0 my ($self) = @_;
77 0   0     0 return $self->id // 'node_without_id(addr=' . refaddr($self) . ')';
78             }
79              
80             # Since we have overloaded stringification, we must overload == as well,
81             # so you can use "if ($nodeA == $nodeB){...}".
82             sub equals {
83 0     0 1 0 my ($self, $node) = @_;
84             #return ref($node) && $node->id eq $self->id;
85 0   0     0 return ref($node) && refaddr($node) == refaddr($self);
86             }
87              
88             sub _not_equals {
89 0     0   0 my ($self, $node) = @_;
90 0         0 return !$self->equals($node);
91             }
92              
93             sub _index_my_id {
94 261     261   477 my $self = shift;
95             # It is possible that get_document is undefined at the moment (for example
96             # if we are reading one large document and splitting it to many small documents).
97             # Then there is nothing to index (and everything will be probably indexed
98             # later when the document is created).
99 261         783 my $document = $self->get_document();
100 261 50       693 if (defined($document))
101             {
102 261         6779 $document->index_node_by_id( $self->id, $self );
103             }
104 261         618 return;
105             }
106              
107             sub _caller_signature {
108 0     0   0 my $level = 1;
109 0         0 my ($package, $filename, $line) = caller;
110 0         0 while ($package =~ /^Treex::Core/){
111 0         0 ($package, $filename, $line) = caller $level++;
112             }
113 0         0 $package =~ s/^Treex::Block:://;
114 0         0 return "$package#$line";
115             }
116              
117             # ---- access to attributes ----
118              
119             # unlike attr (implemented in Treex::PML::Instance::get_data)
120             # get_attr implements only "plain" and "nested hash" attribute names,
121             # i.e. no XPath-like expressions (a/aux.rf[3]) are allowed.
122             # This results in much faster code.
123             sub get_attr {
124 1426     1426 1 2614 my ( $self, $attr_name ) = @_;
125 1426 50       2843 log_fatal('Incorrect number of arguments') if @_ != 2;
126 1426         2001 my $val = $self;
127 1426         3393 for my $step ( split /\//, $attr_name ) {
128 1656 100       3191 if ( !defined $val ) {
129 206 50       616 log_fatal "Attribute '$attr_name' contains strange symbols."
130             . " For XPath like constructs (e.g. 'a/aux.rf[3]') use the 'attr' method."
131             if $attr_name =~ /[^-\w\/.]/;
132             }
133 1656         3142 $val = $val->{$step};
134             }
135 1426         3696 return $val;
136             }
137              
138 24     24   290 use Treex::PML::Factory;
  24         66  
  24         158105  
139              
140             sub set_attr {
141 138     138 1 378 my ( $self, $attr_name, $attr_value ) = @_;
142 138 50       393 log_fatal('Incorrect number of arguments') if @_ != 3;
143 138 50       557 if ( $attr_name eq 'id' ) {
    100          
144 0 0 0     0 if ( not defined $attr_value or $attr_value eq '' ) {
145 0         0 log_fatal 'Setting undefined or empty ID is not allowed';
146             }
147 0         0 $self->get_document->index_node_by_id( $attr_value, $self );
148             }
149             elsif ( ref($attr_value) eq 'ARRAY' ) {
150 9         27 $attr_value = Treex::PML::List->new( @{$attr_value} );
  9         65  
151             }
152              
153 138 100       638 if ($attr_name =~ /\.rf$/){
    100          
154 12         54 my $document = $self->get_document();
155              
156             # Delete previous back references
157 12         38 my $old_value = $self->get_attr($attr_name);
158 12 100       32 if ($old_value) {
159 5 100 66     36 if ( ref $old_value eq 'Treex::PML::List' && @$old_value ) {
160 2         63 $document->remove_backref( $attr_name, $self->id, $old_value );
161             }
162             else {
163 3         85 $document->remove_backref( $attr_name, $self->id, [$old_value] );
164             }
165             }
166              
167             # Set new back references
168 12 100       43 my $ids = ref($attr_value) eq 'Treex::PML::List' ? $attr_value : [$attr_value];
169 12         324 $document->index_backref( $attr_name, $self->id, $ids );
170             }
171             elsif ($attr_name eq 'alignment'){
172 8         23 my $document = $self->get_document();
173 8 100       29 if ($self->{alignment}){
174 4         7 my @old_ids = map { $_->{'counterpart.rf'} } @{$self->{alignment}};
  10         26  
  4         9  
175 4         100 $document->remove_backref( 'alignment', $self->id, \@old_ids );
176             }
177 8 50 33     47 if ($attr_value && @$attr_value){
178 8         19 my @new_ids = map { $_->{'counterpart.rf'} } @$attr_value;
  12         35  
179 8         210 $document->index_backref( $attr_name, $self->id, \@new_ids );
180             }
181             }
182              
183             # TODO: most attributes are set by Moose setters,
184             # e.g. $anode->set_form("Hi") does not call set_attr.
185             # We would need to redefine all the setter to fill wild->{edited_by}.
186 138 50       411 if ($LOG_EDITS){
187 0         0 my $signature = $self->wild->{edited_by};
188 0 0       0 if ($signature) {$signature .= "\n";}
  0         0  
189 0         0 else {$signature = '';}
190 0   0     0 my $a_value = $attr_value // 'undef';
191 0         0 $signature .= "$attr_name=$a_value ". $self->_caller_signature();
192 0         0 $self->wild->{edited_by} = $signature;
193             }
194              
195             #simple attributes can be accessed directly
196 138 100 100     1138 return $self->{$attr_name} = $attr_value if $attr_name =~ /^[\w\.]+$/ || $attr_name eq '#name';
197 8 50       35 log_fatal "Attribute '$attr_name' contains strange symbols."
198             . " No XPath like constructs (e.g. 'a/aux.rf[3]') are allowed."
199             if $attr_name =~ /[^-\w\/.]/;
200              
201 8         17 my $val = $self;
202 8         38 my @steps = split /\//, $attr_name;
203 8         17 while (1) {
204 16         33 my $step = shift @steps;
205 16 100       67 if (@steps) {
206 8 100       33 if ( !defined( $val->{$step} ) ) {
207 4         41 $val->{$step} = Treex::PML::Factory->createStructure();
208             }
209 8         118 $val = $val->{$step};
210             }
211             else {
212 8         53 return $val->{$step} = $attr_value;
213             }
214             }
215 0         0 return;
216             }
217              
218             sub get_deref_attr {
219 0     0 1 0 my ( $self, $attr_name ) = @_;
220 0 0       0 log_fatal('Incorrect number of arguments') if @_ != 2;
221 0         0 my $attr_value = $self->get_attr($attr_name);
222              
223 0 0       0 return if !$attr_value;
224 0         0 my $document = $self->get_document();
225 0 0       0 return [ map { $document->get_node_by_id($_) } @{$attr_value} ]
  0         0  
  0         0  
226             if ref($attr_value) eq 'Treex::PML::List';
227 0         0 return $document->get_node_by_id($attr_value);
228             }
229              
230             sub set_deref_attr {
231 0     0 1 0 my ( $self, $attr_name, $attr_value ) = @_;
232 0 0       0 log_fatal('Incorrect number of arguments') if @_ != 3;
233              
234             # If $attr_value is an array of nodes
235 0 0       0 if ( ref($attr_value) eq 'ARRAY' ) {
236 0         0 my @list = map { $_->id } @{$attr_value};
  0         0  
  0         0  
237 0         0 $attr_value = Treex::PML::List->new(@list);
238             }
239              
240             # If $attr_value is just one node
241             else {
242 0         0 $attr_value = $attr_value->id;
243             }
244              
245             # Set the new reference(s)
246 0         0 $self->set_attr( $attr_name, $attr_value );
247 0         0 return;
248             }
249              
250             sub get_bundle {
251 1158 50   1158 1 4423 log_fatal 'Incorrect number of arguments' if @_ != 1;
252 1158         1729 my $self = shift;
253 1158         2539 return $self->get_zone->get_bundle;
254             }
255              
256             # reference to embedding zone is stored only with tree root, not with nodes
257             sub get_zone {
258 1170 50   1170 1 2585 log_fatal 'Incorrect number of arguments' if @_ != 1;
259 1170         1839 my $self = shift;
260 1170         1677 my $zone;
261 1170 100       2533 if ( $self->is_root ) {
262 394         14546 $zone = $self->_get_zone;
263             }
264             else {
265 776         4266 $zone = $self->get_root->_get_zone; ## no critic (ProtectPrivateSubs)
266             }
267              
268 1170 50       3496 log_fatal "a node (" . $self->id . ") can't reveal its zone" if !$zone;
269 1170         4349 return $zone;
270              
271             }
272              
273             sub remove {
274 26     26 1 811 my ($self, $arg_ref) = @_;
275 26 50       81 if ( $self->is_root ) {
276 0         0 log_fatal 'Tree root cannot be removed using $root->remove().'
277             . ' Use $zone->remove_tree($layer) instead';
278             }
279 26         211 my $root = $self->get_root();
280 26         255 my $document = $self->get_document();
281              
282 26         98 my @children = $self->get_children();
283 26 100       81 if (@children){
284 9         23 my $what_to_do = 'remove';
285 9 0 33     29 if ($arg_ref && $arg_ref->{children}){
286 0         0 $what_to_do = $arg_ref->{children};
287             }
288 9 50       39 if ($what_to_do =~ /^rehang/){
289 0         0 foreach my $child (@children){
290 0         0 $child->set_parent($self->get_parent);
291             }
292             }
293 9 50       32 if ($what_to_do =~ /warn$/){
294 0         0 log_warn $self->get_address . " is being removed by remove({children=>$what_to_do}), but it has (unexpected) children";
295             }
296             }
297              
298             # Remove the subtree from the document's indexing table
299 26         88 my @to_remove = ( $self, $self->get_descendants );
300 26         67 foreach my $node ( @to_remove) {
301 69 50       1830 if ( defined $node->id ) {
302 69         252 $document->_remove_references_to_node( $node );
303 69         1753 $document->index_node_by_id( $node->id, undef );
304             }
305             }
306              
307             # Disconnect the node from its parent (& siblings) and delete all attributes
308             # It actually does: $self->cut(); undef %$_ for ($self->descendants(), $self);
309 26         164 $self->destroy;
310              
311             # TODO: order normalizing can be done in a more efficient way
312             # (update just the following ords)
313 26         924 $root->_normalize_node_ordering();
314              
315             # By reblessing we make sure that
316             # all methods called on removed nodes will result in fatal errors.
317 26         67 foreach my $node (@to_remove){
318 69         149 bless $node, 'Treex::Core::Node::Deleted';
319             }
320 26         106 return;
321             }
322              
323             # Return all nodes that have a reference of the given type (e.g. 'alignment', 'a/lex.rf') to this node
324             sub get_referencing_nodes {
325 24     24 1 73 my ( $self, $type, $lang, $sel ) = @_;
326 24         65 my $doc = $self->get_document;
327 24         622 my $refs = $doc->get_references_to_id( $self->id );
328 24 100 66     157 return if ( !$refs || !$refs->{$type} );
329 22 50 33     86 if ((defined $lang) && (defined $sel)) {
330 0         0 my @ref_filtered_by_tree;
331 0 0       0 if ($sel eq q() ) {
332 0         0 @ref_filtered_by_tree = grep { /(a|t)\_tree\-$lang\-.+/; }@{ $refs->{$type} };
  0         0  
  0         0  
333             }
334             else {
335 0         0 @ref_filtered_by_tree = grep { /(a|t)\_tree\-$lang\_$sel\-.+/; }@{ $refs->{$type} };
  0         0  
  0         0  
336             }
337 0         0 return map { $doc->get_node_by_id($_) } @ref_filtered_by_tree;
  0         0  
338             }
339 22         41 return map { $doc->get_node_by_id($_) } @{ $refs->{$type} };
  22         67  
  22         70  
340             }
341              
342             # Remove a reference of the given type to the given node. This will not remove a reverse reference from document
343             # index, since it is itself called when removing reverse references; use the API methods for the individual
344             # references if you want to keep reverse references up-to-date.
345             sub remove_reference {
346 6     6 0 24 my ( $self, $type, $id ) = @_;
347              
348 6 50       21 if ( $type eq 'alignment' ) { # handle alignment links separately
349              
350 0         0 my $links = $self->get_attr('alignment');
351              
352 0 0       0 if ($links) {
353 0         0 $self->set_attr( 'alignment', [ grep { $_->{'counterpart.rf'} ne $id } @{$links} ] );
  0         0  
  0         0  
354             }
355             }
356             else {
357 6         23 my $attr = $self->get_attr($type);
358 6 50       41 log_fatal "undefined attr $type (id=$id)" if !defined $attr;
359              
360 6 100 100     31 if ( $attr eq $id || scalar( @{$attr} ) <= 1 ) { # single-value attributes
  3         14  
361 5         22 $self->set_attr( $type, undef );
362             }
363             else {
364 1         8 $attr->delete_value($id); # TODO : will it be always a Treex::PML::List? Looks like it works.
365             }
366             }
367 6         50 return;
368             }
369              
370              
371             sub fix_pml_type {
372 262 50   262 1 662 log_fatal 'Incorrect number of arguments' if @_ != 1;
373 262         453 my $self = shift;
374 262 100       947 if ( not $self->type() ) {
375 261         3035 my $type = $self->get_pml_type_name();
376 261 50       1485 if ( not $type ) {
377 0         0 log_warn "No PML type recognized for node $self";
378 0         0 return;
379             }
380 261         696 my $fs_file = $self->get_document()->_pmldoc;
381 261         1138 $self->set_type_by_name( $fs_file->metaData('schema'), $type );
382             }
383 262         13999 return;
384             }
385              
386             sub get_pml_type_name {
387 0 0   0 1 0 log_fatal 'Incorrect number of arguments' if @_ != 1;
388 0         0 my $self = shift;
389 0         0 return;
390             }
391              
392             sub get_layer {
393 19 50   19 1 68 log_fatal 'Incorrect number of arguments' if @_ != 1;
394 19         38 my $self = shift;
395 19 50       115 if ( ref($self) =~ /Node::(\w)$/ ) {
396 19         106 return lc($1);
397             }
398             else {
399 0         0 log_fatal "Cannot recognize node's layer: $self";
400             }
401             }
402              
403             sub language {
404 7 50   7 1 19 log_fatal 'Incorrect number of arguments' if @_ != 1;
405 7         14 my $self = shift;
406 7         20 return $self->get_zone()->language;
407             }
408              
409             sub selector {
410 0 0   0 1 0 log_fatal 'Incorrect number of arguments' if @_ != 1;
411 0         0 my $self = shift;
412 0         0 return $self->get_zone()->selector;
413             }
414              
415             sub create_child {
416 183     183 1 5736 my $self = shift;
417              
418             #NOT VALIDATED INTENTIONALLY - passing args to to new (and it's also black magic, so I'm not touching it)
419              
420             # TODO:
421             #my $new_node = ( ref $self )->new(@_);
422             # Previous line is very strange and causes errors which are hard to debug.
423             # Magically, it works on UFAL machines, but nowhere else - I don't know why.
424             # Substituting the hash by hashref is a workaround,
425             # but the black magic is still there.
426 183         295 my $arg_ref;
427 183 100 66     920 if ( scalar @_ == 1 && ref $_[0] eq 'HASH' ) {
    50          
428 75         162 $arg_ref = $_[0];
429             }
430             elsif ( @_ % 2 ) {
431 0         0 log_fatal "Odd number of elements for create_child";
432             }
433             else {
434 108         265 $arg_ref = {@_};
435             }
436              
437             # Structured attributes (e.g. morphcat/pos) must be handled separately
438             # TODO: And also attributes which don't have accessors (those are not Moose attributes).
439             # Note: mlayer_pos was not added to Treex::Core::Node::T because it goes
440             # against the "tectogrammatical ideology" and we use it as a temporary hack.
441 183         314 my %structured_attrs;
442 183         277 foreach my $attr ( keys %{$arg_ref} ) {
  183         615  
443 116 100 66     766 if ( $attr =~ m{/} || $attr eq 'mlayer_pos' || $attr eq '#name') {
      100        
444 62         206 $structured_attrs{$attr} = delete $arg_ref->{$attr};
445             }
446             }
447              
448 183         438 $arg_ref->{_called_from_core_} = 1;
449 183         5722 my $new_node = ( ref $self )->new($arg_ref);
450 183         1954 $new_node->set_parent($self);
451              
452 183         599 my $new_id = $self->generate_new_id();
453 183         5188 $new_node->set_id($new_id);
454              
455 183         419 foreach my $attr ( keys %structured_attrs ) {
456 62         182 $new_node->set_attr( $attr, $structured_attrs{$attr} );
457             }
458              
459             # my $type = $new_node->get_pml_type_name();
460             # return $new_node if !defined $type;
461             # my $fs_file = $self->get_bundle->get_document()->_pmldoc;
462             # $self->set_type_by_name( $fs_file->metaData('schema'), $type );
463              
464 183         626 $new_node->fix_pml_type();
465              
466             # Remember which module (Treex block) and line number in its source code are responsible for creating this node.
467 183 50       516 if ($LOG_NEW){
468 0         0 $new_node->wild->{created_by} = $self->_caller_signature();
469             }
470              
471 183         782 return $new_node;
472             }
473              
474             #************************************
475             #---- TREE NAVIGATION ------
476              
477             sub get_document {
478 1008 50   1008 1 2454 log_fatal 'Incorrect number of arguments' if @_ != 1;
479 1008         1552 my $self = shift;
480 1008         2412 my $bundle = $self->get_bundle();
481 1008 50       2811 log_fatal('Cannot call get_document on a node which is in no bundle') if !defined $bundle;
482 1008         32744 return $bundle->get_document();
483             }
484              
485             sub get_root {
486 1144 50   1144 1 5458 log_fatal 'Incorrect number of arguments' if @_ != 1;
487 1144         1767 my $self = shift;
488 1144         2971 return $self->root();
489             }
490              
491             sub is_root {
492 1533 50   1533 1 7109 log_fatal 'Incorrect number of arguments' if @_ != 1;
493 1533         2318 my $self = shift;
494 1533         3780 return !$self->parent;
495             }
496              
497             sub is_leaf {
498 0 0   0 1 0 log_fatal 'Incorrect number of arguments' if @_ != 1;
499 0         0 my $self = shift;
500 0         0 return not $self->firstson;
501             }
502              
503             sub get_parent {
504 547 50   547 1 2468 log_fatal 'Incorrect number of arguments' if @_ != 1;
505 547         857 my $self = shift;
506 547         1431 return $self->parent;
507             }
508              
509             sub set_parent {
510 189     189 1 348 my $self = shift;
511 189         948 my ($parent) = pos_validated_list(
512             \@_,
513             { isa => 'Treex::Core::Node' },
514             );
515              
516             # TODO check for this (but set_parent is called also from create_child)
517             #if ($self->get_document() != $parent->get_document()) {
518             # log_fatal("Cannot move a node from one document to another");
519             #}
520              
521             # We cannot detach a node by setting an undefined parent. The if statement below will die.
522             # Let's inform the user where the bad call is.
523 189 50       652 log_fatal( 'Cannot attach the node ' . $self->id . ' to an undefined parent' ) if ( !defined($parent) );
524 189 50 33     1129 if ( $self == $parent || $CHECK_FOR_CYCLES && $parent->is_descendant_of($self) ) {
      33        
525 0         0 my $id = $self->id;
526 0         0 my $p_id = $parent->id;
527 0         0 log_fatal("Attempt to set parent of $id to the node $p_id, which would lead to a cycle.");
528             }
529              
530             # TODO: Too much FSlib (aka Treex::PML) here
531 189         784 $self->cut();
532 189         2761 my $fsfile = $parent->get_document()->_pmldoc;
533 189         781 my @fschildren = $parent->children();
534 189 100       3293 if (@fschildren) {
535 103         402 Treex::PML::PasteAfter( $self, $fschildren[-1] );
536             }
537             else {
538 86         432 Treex::PML::Paste( $self, $parent, $fsfile->FS() );
539             }
540              
541 189         8393 return;
542             }
543              
544             sub _check_switches {
545              
546             #This method may be replaced by subtype and checked as parameter
547 132     132   223 my $self = shift;
548 132         452 my ($arg_ref) = pos_validated_list(
549             \@_,
550             { isa => 'Maybe[HashRef]' },
551             );
552              
553             # Check for role Ordered
554             log_fatal('This type of node does not support ordering')
555             if (
556 132 50 66 208   1056 ( $arg_ref->{ordered} || any { $arg_ref->{ $_ . '_only' } } qw(first last preceding following) )
  208   66     574  
557             &&
558             !$self->does('Treex::Core::Node::Ordered')
559             );
560              
561             # Check switches for not allowed combinations
562             log_fatal('Specified both preceding_only and following_only.')
563 132 0 33     20055 if $arg_ref->{preceding_only} && $arg_ref->{following_only};
564             log_fatal('Specified both first_only and last_only.')
565 132 0 33     377 if $arg_ref->{first_only} && $arg_ref->{last_only};
566              
567             # Check for explicit "ordered" when not needed (possible typo)
568             # DZ: Turning off. This is an annoying warning when I want to explicitly say in my code that the result will be ordered and accessing the first/last item is meaningful.
569             #log_warn('Specifying (first|last|preceding|following)_only implies ordered.')
570             # if $arg_ref->{ordered}
571             # && any { $arg_ref->{ $_ . '_only' } } qw(first last preceding following);
572              
573             # Check for unknown switches
574 132     172   551 my $unknown = first { $_ !~ $_SWITCHES_REGEX } keys %{$arg_ref};
  172         1347  
  132         671  
575 132 50       1693 log_warn("Unknown switch $unknown") if defined $unknown;
576              
577 132         313 return;
578             }
579              
580             # Shared processing of switches: ordered, (preceding|following|first|last)_only
581             # for subs get_children, get_descendants and get_siblings.
582             # This is quite an uneffective implementation in case of e.g. first_only
583             sub _process_switches {
584 132     132   259 my $self = shift;
585 132         585 my ( $arg_ref, @nodes ) = pos_validated_list(
586             \@_,
587             { isa => 'Maybe[HashRef]' },
588             MX_PARAMS_VALIDATE_ALLOW_EXTRA => 1,
589             );
590              
591             # Check for unknown switches and not allowed combinations
592 132         610 $self->_check_switches($arg_ref);
593              
594             # Add this node if add_self
595 132 100       379 if ( $arg_ref->{add_self} ) {
596 92         196 push @nodes, $self;
597             }
598              
599             # Sort nodes if needed
600 132 100 66 208   3173 if (( $arg_ref->{ordered} || any { $arg_ref->{ $_ . '_only' } } qw(first last preceding following) )
  208   100     528  
      100        
601             && @nodes && defined $nodes[0]->ord
602             )
603             {
604 52         179 @nodes = sort { $a->ord() <=> $b->ord() } @nodes;
  167         4218  
605             }
606              
607             # Leave preceding/following only if needed
608 132 50       564 if ( $arg_ref->{preceding_only} ) {
    50          
609 0         0 @nodes = grep { $_->ord() <= $self->ord } @nodes;
  0         0  
610             }
611             elsif ( $arg_ref->{following_only} ) {
612 0         0 @nodes = grep { $_->ord() >= $self->ord } @nodes;
  0         0  
613             }
614              
615             # first_only / last_only
616 132 50       313 return $nodes[0] if $arg_ref->{first_only};
617 132 50       321 return $nodes[-1] if $arg_ref->{last_only};
618 132         676 return @nodes;
619             }
620              
621             sub get_children {
622 172     172 1 374 my $self = shift;
623 172         842 my ($arg_ref) = pos_validated_list(
624             \@_,
625             { isa => 'Maybe[HashRef]', optional => 1 },
626             );
627              
628 172         864 my @children = $self->children();
629 172 100       3494 return @children if !$arg_ref;
630 1         7 return $self->_process_switches( $arg_ref, @children );
631             }
632              
633             sub get_descendants {
634 178     178 1 3183 my $self = shift;
635 178         969 my ($arg_ref) = pos_validated_list(
636             \@_,
637             { isa => 'Maybe[HashRef]', optional => 1 },
638             );
639              
640 178         431 my @descendants;
641 178 50 66     761 if ( $arg_ref && $arg_ref->{except} ) {
642 0         0 my $except_node = delete $arg_ref->{except};
643 0 0       0 return () if $self == $except_node;
644             @descendants = map {
645 0         0 $_->get_descendants( { except => $except_node, add_self => 1 } )
  0         0  
646             } $self->get_children();
647             }
648             else {
649 178         541 @descendants = $self->descendants();
650             }
651 178 100       593 return @descendants if !$arg_ref;
652 106         414 return $self->_process_switches( $arg_ref, @descendants );
653             }
654              
655             sub get_siblings {
656 16     16 1 2606 my $self = shift;
657 16         101 my ($arg_ref) = pos_validated_list(
658             \@_,
659             { isa => 'Maybe[HashRef]', optional => 1 },
660             );
661 16         66 my $parent = $self->get_parent();
662 16 100       102 return () if !$parent;
663 12         39 my @siblings = grep { $_ ne $self } $parent->get_children();
  40         116  
664 12 50       76 return @siblings if !$arg_ref;
665 0         0 return $self->_process_switches( $arg_ref, @siblings );
666             }
667              
668 0     0 1 0 sub get_left_neighbor { return $_[0]->get_siblings( { preceding_only => 1, last_only => 1 } ); }
669 0     0 1 0 sub get_right_neighbor { return $_[0]->get_siblings( { following_only => 1, first_only => 1 } ); }
670              
671             sub is_descendant_of {
672 215     215 1 1645 my $self = shift;
673 215         750 my ($another_node) = pos_validated_list(
674             \@_,
675             { isa => 'Treex::Core::Node' },
676             );
677              
678 215         652 my $parent = $self->get_parent();
679 215         1035 while ($parent) {
680 212 100       749 return 1 if $parent == $another_node;
681 208         371 $parent = $parent->get_parent();
682             }
683 211         1111 return 0;
684             }
685             sub dominates {
686 0     0 0 0 my $self = shift;
687 0         0 my $another_node = shift;
688 0         0 return $another_node->is_descendant_of($self);
689             }
690              
691             #************************************
692             #---- OTHER ------
693              
694             sub get_depth {
695 12 50   12 1 1234 log_fatal 'Incorrect number of arguments' if @_ != 1;
696 12         27 my $self = shift;
697 12         20 my $depth = 0;
698 12         37 while ( $self = $self->get_parent() ) {
699 12         65 $depth++;
700             }
701 12         92 return $depth;
702             }
703              
704             # This is called from $node->remove()
705             # so it must be defined in this class,
706             # but it is overriden in Treex::Core::Node::Ordered.
707       16     sub _normalize_node_ordering {
708             }
709              
710             sub get_address {
711 0 0   0 1 0 log_fatal 'Incorrect number of arguments' if @_ != 1;
712 0         0 my $self = shift;
713 0         0 my $id = $self->id;
714 0         0 my $bundle = $self->get_bundle();
715 0         0 my $doc = $bundle->get_document();
716 0   0     0 my $file = $doc->loaded_from || ( $doc->full_filename . '.treex' );
717 0         0 my $position = $bundle->get_position() + 1;
718              
719             #my $filename = Cwd::abs_path($file);
720 0         0 return "$file##$position.$id";
721             }
722              
723             # The MISC attributes from CoNLL-U files are stored as wild attributes.
724             # These methods should be in a Universal Dependencies related role but we don't have one.
725             # get_misc() returns a list of MISC attributes (possibly empty list)
726             sub get_misc
727             {
728 0     0 0 0 my $self = shift;
729 0         0 my @misc;
730 0         0 my $wild = $self->wild();
731 0 0 0     0 if (exists($wild->{misc}) && defined($wild->{misc}))
732             {
733 0         0 @misc = split(/\|/, $wild->{misc});
734             }
735 0         0 return @misc;
736             }
737              
738             # set_misc() takes a list of MISC attributes (possibly empty list)
739             sub set_misc
740             {
741 0     0 0 0 my $self = shift;
742 0         0 my @misc = @_;
743 0         0 my $wild = $self->wild();
744 0 0       0 if (scalar(@misc) > 0)
745             {
746 0         0 $wild->{misc} = join('|', @misc);
747             }
748             else
749             {
750 0         0 delete($wild->{misc});
751             }
752             }
753              
754             # set_misc_attr() takes an attribute name and value; assumes that MISC elements are attr=value pairs; replaces first or pushes at the end
755             sub set_misc_attr
756             {
757 0     0 0 0 my $self = shift;
758 0         0 my $attr = shift;
759 0         0 my $value = shift;
760 0 0 0     0 if (defined($attr) && defined($value))
761             {
762 0         0 my @misc = $self->get_misc();
763 0         0 my $found = 0;
764 0         0 for(my $i = 0; $i <= $#misc; $i++)
765             {
766 0 0 0     0 if ($misc[$i] =~ m/^(.+?)=(.+)$/ && $1 eq $attr)
767             {
768 0 0       0 if ($found)
769             {
770 0         0 splice(@misc, $i--, 1);
771             }
772             else
773             {
774 0         0 $misc[$i] = "$attr=$value";
775 0         0 $found = 1;
776             }
777             }
778             }
779 0 0       0 if (!$found)
780             {
781 0         0 push(@misc, "$attr=$value");
782             }
783 0         0 $self->set_misc(@misc);
784             }
785             }
786              
787             # Empty DESTROY method is a hack to get rid of the "Deep recursion warning"
788             # in Treex::PML::Node::DESTROY and MooseX::NonMoose::Meta::Role::Class::_check_superclass_destructor.
789             # Without this hack, you get the warning after creating a node with 99 or more children.
790             # Deep recursion on subroutine "Class::MOP::Method::execute" at .../5.12.2/MooseX/NonMoose/Meta/Role/Class.pm line 183.
791       0     sub DESTROY {
792             }
793              
794             #*************************************
795             #---- DEPRECATED & QUESTIONABLE ------
796              
797             sub generate_new_id { #TODO move to Core::Document?
798 183 50   183 1 499 log_fatal 'Incorrect number of arguments' if @_ != 1;
799 183         309 my $self = shift;
800 183         433 my $doc = $self->get_document;
801              
802 183         5326 my $latest_node_number = $doc->_latest_node_number;
803              
804 183         385 my $new_id;
805              
806             #$self->get_root->id =~ /(.+)root/;
807             #my $id_base = $1 || "";
808             my $id_base;
809 183 50       549 if ( $self->get_root->id =~ /(.+)root/ ) {
810 183         460 $id_base = $1;
811             }
812             else {
813 0         0 $id_base = q();
814             }
815              
816 183         324 while (1) {
817 183         310 $latest_node_number++;
818 183         535 $new_id = "${id_base}n$latest_node_number";
819 183 50       629 last if !$doc->id_is_indexed($new_id);
820              
821             }
822              
823 183         6383 $doc->_set_latest_node_number($latest_node_number);
824              
825 183         488 return $new_id;
826             }
827              
828             sub add_to_listattr {
829 0     0 1 0 my $self = shift;
830 0         0 my ( $attr_name, $attr_value ) = pos_validated_list(
831             \@_,
832             { isa => 'Str' },
833             { isa => 'Any' },
834             );
835              
836 0         0 my $list = $self->attr($attr_name);
837 0 0 0     0 log_fatal("Attribute $attr_name is not a list!")
838             if !defined $list || ref($list) ne 'Treex::PML::List';
839 0         0 my @new_list = @{$list};
  0         0  
840 0 0       0 if ( ref($attr_value) eq 'ARRAY' ) {
841 0         0 push @new_list, @{$attr_value};
  0         0  
842             }
843             else {
844 0         0 push @new_list, $attr_value;
845             }
846 0         0 return $self->set_attr( $attr_name, Treex::PML::List->new(@new_list) );
847             }
848              
849             # Get more attributes at once
850             sub get_attrs {
851 0     0 1 0 my $self = shift;
852 0         0 my @attr_names = pos_validated_list(
853             \@_,
854             { isa => 'Any' }, #at least one parameter
855             MX_PARAMS_VALIDATE_ALLOW_EXTRA => 1,
856             );
857              
858 0         0 my @attr_values;
859 0 0       0 if ( ref $attr_names[-1] ) {
860 0         0 my $arg_ref = pop @attr_names;
861 0         0 my $change_undefs_to = $arg_ref->{undefs};
862             @attr_values = map {
863 0 0       0 defined $self->get_attr($_) ? $self->get_attr($_) : $change_undefs_to
  0         0  
864             } @attr_names;
865             }
866             else {
867 0         0 @attr_values = map { $self->get_attr($_) } @attr_names;
  0         0  
868             }
869              
870 0         0 return @attr_values;
871             }
872              
873             # Return all attributes of the given node (sub)type that contain references
874             sub _get_reference_attrs {
875 134     134   229 my ($self) = @_;
876 134         265 return ();
877             }
878              
879             # Return IDs of all nodes to which there are reference links from this node (must be overridden in
880             # the respective node types)
881             sub _get_referenced_ids {
882 372     372   769 my ($self) = @_;
883 372         706 my $ret = {};
884              
885             # handle alignment separately
886 372         1144 my $links_rf = $self->get_attr('alignment');
887 372 50       857 $ret->{alignment} = [ map { $_->{'counterpart.rf'} } @{$links_rf} ] if ($links_rf);
  0         0  
  0         0  
888              
889             # all other references
890 372         1358 foreach my $ref_attr ( $self->_get_reference_attrs() ) {
891 868 100       1574 my $val = $self->get_attr($ref_attr) or next;
892 5 100       23 if ( !ref $val ) { # single-valued
893 3         14 $ret->{$ref_attr} = [$val];
894             }
895             else {
896 2         11 $ret->{$ref_attr} = $val;
897             }
898             }
899 372         969 return $ret;
900             }
901              
902              
903             # ---------------------
904              
905             # changing the functionality of Treex::PML::Node's following() so that it traverses all
906             # nodes in all trees in all zones (needed for search in TrEd)
907              
908             sub following {
909 298     298 1 1445 my ( $self ) = @_;
910              
911 298         644 my $pml_following = Treex::PML::Node::following(@_);
912              
913 298 100       3248 if ( $pml_following ) {
914 173         334 return $pml_following;
915             }
916              
917             else {
918 125 100       469 my $bundle = ( ref($self) eq 'Treex::Core::Bundle' ) ? $self : $self->get_bundle;
919              
920             my @all_trees = map {
921 125 50       441 ref($_) ne 'Treex::PML::Struct'
  410         1569  
922             ? $_->get_all_trees
923             : ()
924             } $bundle->get_all_zones;
925              
926 125 100       399 if ( ref($self) eq 'Treex::Core::Bundle' ) {
927 40         134 return $all_trees[0];
928             }
929              
930             else {
931 85         295 my $my_root = $self->get_root;
932 85         796 foreach my $index ( 0..$#all_trees ) {
933 443 100       1025 if ( $all_trees[$index] eq $my_root ) {
934 85         344 return $all_trees[$index+1];
935             }
936             }
937 0         0 log_fatal "Node belongs to no tree: this should never happen";
938             }
939             }
940             }
941              
942             # This is copied from Treex::PML::Node.
943             # Using Treex::PML::Node::following is faster than recursion
944             # and it does not cause "deep recursion" warnings.
945             sub descendants {
946 204     204 1 1171 my $self = $_[0];
947 204         386 my @kin = ();
948 204         740 my $desc = $self->Treex::PML::Node::following($self);
949 204         1950 while ($desc) {
950 515         4383 push @kin, $desc;
951 515         963 $desc = $desc->Treex::PML::Node::following($self);
952             }
953 204         1761 return @kin;
954             }
955              
956             # TODO: How to do this in an elegant way?
957             # Unless we find a better way, we must disable two perlcritics
958             package Treex::Core::Node::Removed; ## no critic (ProhibitMultiplePackages)
959             $Treex::Core::Node::Removed::VERSION = '2.20210102';
960 24     24   301 use Treex::Core::Log;
  24         68  
  24         6722  
961              
962             sub AUTOLOAD { ## no critic (ProhibitAutoloading)
963 3     3   975 our $AUTOLOAD;
964 3 50       65 if ( $AUTOLOAD !~ /DESTROY$/ ) {
965 0           log_fatal("You cannot call any methods on removed nodes, but have called $AUTOLOAD");
966             }
967             }
968              
969             package Treex::Core::Node; ## no critic (ProhibitMultiplePackages)
970              
971             __PACKAGE__->meta->make_immutable;
972              
973             1;
974              
975             __END__
976              
977             ##-- begin proposal
978             # Example usage:
979             # Treex::Core::Node::T methods get_lex_anode and get_aux_anodes could use:
980             # my $a_lex = $t_node->get_r_attr('a/lex.rf'); # returns the node or undef
981             # my @a_aux = $t_node->get_r_attr('a/aux.rf'); # returns the nodes or ()
982             sub get_r_attr {
983             my ( $self, $attr_name ) = @_;
984             log_fatal('Incorrect number of arguments') if @_ != 2;
985             my $attr_value = $self->get_attr($attr_name);
986              
987             return if !$attr_value;
988             my $document = $self->get_document();
989             if (wantarray) {
990             log_fatal("Attribute '$attr_name' is not a list, but get_r_attr() called in a list context.")
991             if ref($attr_value) ne 'Treex::PML::List';
992             return map { $document->get_node_by_id($_) } @{$attr_value};
993             }
994              
995             log_fatal("Attribute $attr_name is a list, but get_r_attr() not called in a list context.")
996             if ref($attr_value) eq 'Treex::PML::List';
997             return $document->get_node_by_id($attr_value);
998             }
999              
1000             # Example usage:
1001             # $t_node->set_r_attr('a/lex.rf', $a_lex);
1002             # $t_node->set_r_attr('a/aux.rf', @a_aux);
1003             sub set_r_attr {
1004             my ( $self, $attr_name, @attr_values ) = @_;
1005             log_fatal('Incorrect number of arguments') if @_ < 3;
1006             my $fs = $self;
1007              
1008             # TODO $fs->type nefunguje - asi protoze se v konstruktorech nenastavuje typ
1009             if ( $fs->type($attr_name) eq 'Treex::PML::List' ) {
1010             my @list = map { $_->id } @attr_values;
1011              
1012             # TODO: overriden Node::N::set_attr is bypassed by this call
1013             return $fs->set_attr( $attr_name, Treex::PML::List->new(@list) );
1014             }
1015             log_fatal("Attribute '$attr_name' is not a list, but set_r_attr() called with @attr_values values.")
1016             if @attr_values > 1;
1017              
1018             # TODO: overriden Node::N::set_attr is bypassed by this call
1019             return $fs->set_attr( $attr_name, $attr_values[0]->id );
1020             }
1021              
1022              
1023              
1024             =for Pod::Coverage BUILD
1025              
1026              
1027             =encoding utf-8
1028              
1029             =head1 NAME
1030              
1031             Treex::Core::Node - smallest unit that holds information in Treex
1032              
1033             =head1 VERSION
1034              
1035             version 2.20210102
1036              
1037             =head1 DESCRIPTION
1038              
1039             This class represents a Treex node.
1040             Treex trees (contained in bundles) are formed by nodes and edges.
1041             Attributes can be attached only to nodes. Edge's attributes must
1042             be stored as the lower node's attributes.
1043             Tree's attributes must be stored as attributes of the root node.
1044              
1045             =head1 METHODS
1046              
1047             =head2 Construction
1048              
1049             =over 4
1050              
1051             =item my $new_node = $existing_node->create_child({lemma=>'house', tag=>'NN' });
1052              
1053             Creates a new node as a child of an existing node. Some of its attribute
1054             can be filled. Direct calls of node constructors (C<< ->new >>) should be avoided.
1055              
1056              
1057             =back
1058              
1059              
1060              
1061             =head2 Access to the containers
1062              
1063             =over 4
1064              
1065             =item my $bundle = $node->get_bundle();
1066              
1067             Returns the L<Treex::Core::Bundle> object in which the node's tree is contained.
1068              
1069             =item my $document = $node->get_document();
1070              
1071             Returns the L<Treex::Core::Document> object in which the node's tree is contained.
1072              
1073             =item get_layer
1074              
1075             Return the layer of this node (I<a>, I<t>, I<n> or I<p>).
1076              
1077             =item get_zone
1078              
1079             Return the zone (L<Treex::Core::BundleZone>) to which this node
1080             (and the whole tree) belongs.
1081              
1082             =item $lang_code = $node->language
1083              
1084             shortcut for C<< $lang_code = $node->get_zone()->language >>
1085              
1086             =item $selector = $node->selector
1087              
1088             shortcut for C<< $selector = $node->get_zone()->selector >>
1089              
1090             =back
1091              
1092              
1093             =head2 Access to attributes
1094              
1095             =over 4
1096              
1097             =item my $value = $node->get_attr($name);
1098              
1099             Returns the value of the node attribute of the given name.
1100              
1101             =item my $node->set_attr($name,$value);
1102              
1103             Sets the given attribute of the node with the given value.
1104             If the attribute name is C<id>, then the document's indexing table
1105             is updated. If value of the type C<List> is to be filled,
1106             then C<$value> must be a reference to the array of values.
1107              
1108             =item my $node2 = $node1->get_deref_attr($name);
1109              
1110             If value of the given attribute is reference (or list of references),
1111             it returns the appropriate node (or a reference to the list of nodes).
1112              
1113             =item my $node1->set_deref_attr($name, $node2);
1114              
1115             Sets the given attribute with C<id> (list of C<id>s) of the given node (list of nodes).
1116              
1117             =item my $node->add_to_listattr($name, $value);
1118              
1119             If the given attribute is list, the given value is appended to it.
1120              
1121             =item my $node->get_attrs(qw(name_of_attr1 name_of_attr2 ...));
1122              
1123             Get more attributes at once.
1124             If the last argument is C<{undefs=E<gt>$value}>, all undefs are substituted
1125             by a C<$value> (typically the value is an empty string).
1126              
1127             =back
1128              
1129              
1130              
1131              
1132             =head2 Access to tree topology
1133              
1134             =over 4
1135              
1136             =item my $parent_node = $node->get_parent();
1137              
1138             Returns the parent node, or C<undef> if there is none (if C<$node> itself is the root)
1139              
1140             =item $node->set_parent($parent_node);
1141              
1142             Makes C<$node> a child of C<$parent_node>.
1143              
1144             =item $node->remove({children=>remove});
1145              
1146             Deletes a node.
1147             Node identifier is removed from the document indexing table.
1148             The removed node cannot be further used.
1149              
1150             Optional argument C<children> in C<$arg_ref> can specify
1151             what to do with children (and all descendants,
1152             i.e. the subtree rooted by the given node) if present:
1153             C<remove>, C<remove_warn>, C<rehang>, C<rehang_warn>.
1154             The default is C<remove> -- remove recursively.
1155             C<rehang> means reattach the children of C<$node> to the parent of C<$node>.
1156             The C<_warn> variants will in addition produce a warning.
1157              
1158             =item my $root_node = $node->get_root();
1159              
1160             Returns the root of the node's tree.
1161              
1162             =item $node->is_root();
1163              
1164             Returns C<true> if the node has no parent.
1165              
1166             =item $node->is_leaf();
1167              
1168             Returns C<true> if the node has no children.
1169              
1170             =item $node1->is_descendant_of($node2);
1171              
1172             Tests whether C<$node1> is among transitive descendants of C<$node2>;
1173              
1174             =back
1175              
1176             Next three methods (for access to children / descendants / siblings)
1177             have an optional argument C<$arg_ref> for specifying switches.
1178             By adding some switches, you can modify the behavior of these methods.
1179             See L<"Switches"> for examples.
1180              
1181             =over
1182              
1183             =item my @child_nodes = $node->get_children($arg_ref);
1184              
1185             Returns an array of child nodes.
1186              
1187             =item my @descendant_nodes = $node->get_descendants($arg_ref);
1188              
1189             Returns an array of descendant nodes ('transitive children').
1190              
1191             =item my @sibling_nodes = $node->get_siblings($arg_ref);
1192              
1193             Returns an array of nodes sharing the parent with the current node.
1194              
1195             =back
1196              
1197             =head3 Switches
1198              
1199             Currently there are 6 switches:
1200              
1201             =over
1202              
1203             =item * ordered
1204              
1205             =item * preceding_only, following_only
1206              
1207             =item * first_only, last_only
1208              
1209             =item * add_self
1210              
1211             =back
1212              
1213             =head4 Examples of usage
1214              
1215             Names of variables in the examples suppose a language with left-to-right script.
1216              
1217             my @ordered_descendants = $node->get_descendants({ordered=>1});
1218             my @self_and_left_children = $node->get_children({preceding_only=>1, add_self=>1});
1219             my @ordered_self_and_children = $node->get_children({ordered=>1, add_self=>1});
1220             my $leftmost_child = $node->get_children({first_only=>1});
1221             my @ordered_siblings = $node->get_siblings({ordered=>1});
1222             my $left_neighbor = $node->get_siblings({preceding_only=>1, last_only=>1});
1223             my $right_neighbor = $node->get_siblings({following_only=>1, first_only=>1});
1224             my $leftmost_sibling_or_self = $node->get_siblings({add_self=>1, first_only=>1});
1225              
1226             =head4 Restrictions
1227              
1228             =over
1229              
1230             =item *
1231              
1232             C<first_only> and C<last_only> switches makes the method return just one item -
1233             a scalar, even if combined with the C<add_self> switch.
1234              
1235             =item *
1236              
1237             Specifying C<(first|last|preceding|following)_only> implies C<ordered>,
1238             so explicit addition of C<ordered> gives a warning.
1239              
1240             =item *
1241              
1242             Specifying both C<preceding_only> and C<following_only> gives an error
1243             (same for combining C<first_only> and C<last_only>).
1244              
1245             =back
1246              
1247             =head4 Shortcuts
1248              
1249             There are shortcuts for comfort of those who use B<left-to-right> scripts:
1250              
1251             =over
1252              
1253             =item my $left_neighbor_node = $node->get_left_neighbor();
1254              
1255             Returns the rightmost node from the set of left siblings (the nearest left sibling).
1256             Actually, this is shortcut for C<$node-E<gt>get_siblings({preceding_only=E<gt>1, last_only=E<gt>1})>.
1257              
1258             =item my $right_neighbor_node = $node->get_right_neighbor();
1259              
1260             Returns the leftmost node from the set of right siblings (the nearest right sibling).
1261             Actually, this is shortcut for C<$node-E<gt>get_siblings({following_only=E<gt>1, first_only=E<gt>1})>.
1262              
1263             =back
1264              
1265             =head2 PML-related methods
1266              
1267             =over
1268              
1269             =item my $type = $node->get_pml_type_name;
1270              
1271             =item $node->fix_pml_type();
1272              
1273             If a node has no PML type, then its type is detected (according
1274             to the node's location) and filled by the PML interface.
1275              
1276             =back
1277              
1278             =head2 References (alignment and other references depending on node subtype)
1279              
1280             =over
1281              
1282             =item my @refnodes = $node->get_referencing_nodes($ref_type);
1283              
1284             Returns an array of nodes referencing this node with the given reference type (e.g. 'alignment', 'a/lex.rf' etc.).
1285              
1286             =back
1287              
1288             =head2 Other methods
1289              
1290             =over 4
1291              
1292             =item $node->generate_new_id();
1293              
1294             Generate new (= so far unindexed) identifier (to be used when creating new
1295             nodes). The new identifier is derived from the identifier of the root
1296             (C<< $node->root >>), by adding suffix C<x1> (or C<x2>, if C<...x1> has already
1297             been indexed, etc.) to the root's C<id>.
1298              
1299             =item my $levels = $node->get_depth();
1300              
1301             Return the depth of the node. The root has depth = 0, its children have depth = 1 etc.
1302              
1303             =item my $address = $node->get_address();
1304              
1305             Return the node address, i.e. file name and node's position within the file,
1306             similarly to TrEd's C<FPosition()> (but the value is only returned, not printed).
1307              
1308             =item $node->equals($another_node)
1309              
1310             This is the internal implementation of overloaded C<==> operator,
1311             which checks whether C<$node == $another_node> (the object instance must be identical).
1312              
1313             =item my $string = $node->to_string()
1314              
1315             This is the internal implementation of overloaded stringification,
1316             so you can use e.g. C<print "There is a node $node.">.
1317             It returns the id (C<$node->id>), but the behavior may be overridden in subclasses.
1318             See L<overload> pragma for details about overloading operators in Perl.
1319              
1320             =back
1321              
1322              
1323             =head1 AUTHORS
1324              
1325             Zdeněk Žabokrtský <zabokrtsky@ufal.mff.cuni.cz>
1326              
1327             Martin Popel <popel@ufal.mff.cuni.cz>
1328              
1329             David Mareček <marecek@ufal.mff.cuni.cz>
1330              
1331             Daniel Zeman <zeman@ufal.mff.cuni.cz>
1332              
1333             Ondřej Dušek <odusek@ufal.mff.cuni.cz>
1334              
1335             =head1 COPYRIGHT AND LICENSE
1336              
1337             Copyright © 2011-2012 by Institute of Formal and Applied Linguistics, Charles University in Prague
1338              
1339             This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.