line
stmt
bran
cond
sub
pod
time
code
1
package yEd::Document;
2
3
1
1
14345
use 5.010;
1
3
1
38
4
1
1
4
use strict;
1
1
1
32
5
1
1
3
use warnings FATAL => 'all';
1
5
1
37
6
1
1
176
use XML::LibXML;
0
0
7
use Carp;
8
use yEd::Label::EdgeLabel;
9
use yEd::Label::NodeLabel;
10
use yEd::Edge::ArcEdge;
11
use yEd::Edge::BezierEdge;
12
use yEd::Edge::GenericEdge;
13
use yEd::Edge::PolyLineEdge;
14
use yEd::Edge::QuadCurveEdge;
15
use yEd::Edge::SplineEdge;
16
use yEd::Node::ShapeNode;
17
use yEd::Node::GenericNode;
18
19
=head1 NAME
20
21
yEd::Document - pure perl API to easily create yEd-loadable Documents from scratch (using XML::LibXML)
22
23
=head1 VERSION
24
25
Version 1.01
26
27
=cut
28
29
our $VERSION = '1.01';
30
31
=head1 DEPENDENCIES
32
33
=over 4
34
35
=item *
36
37
L
38
39
=back
40
41
=head1 INTENTION
42
43
This package is intended to offer a way to create yEd Documents from scratch without having to deal with XML at all.
44
45
It is ment to help automating the task of creating graphical overviews of platforms, workflows, dependencies, ...
46
47
Since it doesn't support all the features of a yEd created Document you can only create Documents, loading Documents into the package is not supported, yet.
48
49
=head1 SUPPORTED FEATURES
50
51
This package and all available features have been developed and tested for yEd version 3.13.
52
53
If you find it doesn't work on another version or a specific platform, please let me know (see sections L and/or L)).
54
55
Object features are supported as described at
56
57
=over 4
58
59
=item *
60
61
L
62
63
=item *
64
65
L
66
67
=item *
68
69
L
70
71
=back
72
73
and its subclasses.
74
75
The Document itself supports basic templating (section L), layers (section L) and id management (section L).
76
77
You can also use relative coords for positioning your entities and use this feature for basic grouping (section L).
78
79
All entities are property based and can also be selected by properties (see L).
80
81
=head1 SYNOPSIS
82
83
This package provides a pure object oriented implementation, so only use functions and properties of yEd::Document and the other types in the context of a blessed instance.
84
85
Minimal example (creating an empty, yEd loadable Document):
86
87
use yEd::Document;
88
# create the document
89
$d = yEd::Document->new();
90
# build the document
91
$xmlstring = $d->buildDocument();
92
# or
93
$d->buildDocument('/mypath/mydocument');
94
95
Example of different ways to create a Node with Label (all have the same effect on the Document):
96
97
# the manual approach
98
my $n = yEd::Node::ShapeNode->new('myid1');
99
$n->x(50);
100
$n->y(150);
101
my $l = yEd::Label::NodeLabel->new('hello world');
102
$n->addLabel($l);
103
$doc->addNode($n);
104
105
# a slightly more user friendly approach
106
# provide the propertys with constructor and don't deal with IDs
107
my $n = yEd::Node::ShapeNode->new($doc->getFreeId(), 'x' => 50, 'y' => 150);
108
# directly add the label
109
my $l = $n->addNewLabel('hello world');
110
$doc->addNode($n);
111
112
# or do it all in two lines
113
my $n = $doc->addNewNode('ShapeNode', 'x' => 50, 'y' => 150);
114
my $l = $n->addNewLabel('hello world');
115
116
# or in one if you don't need the node ref (label ref is returned)
117
my $l = $doc->addNewNode('ShapeNode', 'x' => 50, 'y' => 150)->addNewLabel('hello world');
118
119
# or use templating if you need many copies of the same node
120
my $l = $doc->addNewNodeTemplate('mytemplate', 'ShapeNode', 'x' => 50, 'y' => 150)->addNewLabel('hello world');
121
my $n = $doc->addTemplateNode('mytemplate');
122
# you can use $n to modify the copy (e.g. coords), you can also provide new properties with the call to addTemplateNode()
123
# creating edges can be done in nearly the same ways
124
125
Example of using relative entities and layers (ready to run):
126
127
use strict;
128
use yEd::Document;
129
130
my $d = yEd::Document->new();
131
# play around with these values and note that the edge will always keep its right angles :)
132
my $sx = -15; # -0.5 * default width
133
my $sy = 15; # 0.5 * default height -> lower left corner of source
134
my $tx = 15; # 0.5 * default width
135
my $ty = 15; # 0.5 * default height -> lower right corner of target
136
my $distance = 250;
137
138
# place the absolute "root" node
139
my $grouproot = $d->addNewNode('ShapeNode', 'x' => 1000, 'y' => 1000);
140
# place another node $distance units to its right (or left if negative)
141
my $n = $d->addNewNode('ShapeNode', 'x' => $distance, 'y' => 0, 'relative' => $grouproot);
142
# place an edge on both which will go 50 units down, then $distance units (modified by anchor offsets) right/left (and then up again to connect to its target)
143
my $e = $d->addNewEdge('PolyLineEdge', $grouproot, $n, 'relativeWaypoints' => 1, 'sx' => $sx, 'sy' => $sy, 'tx' => $tx, 'ty' => $ty);
144
$e->waypoints([0,50],[$distance - $sx + $tx,0]);
145
# place a circle on top of $grouproot to make the "group movement" visible in yEd
146
my $c = $d->addNewNode('ShapeNode','shape' => 'ellipse', 'layer' => 1, 'x' => 1000, 'y' => 1000);
147
$d->addNewEdge('PolyLineEdge', $n, $c, 'tArrow' => 'standard', 'sArrow' => 'standard');
148
# you can now move the whole "group" by modifying $grouproot's x and y values (uncomment it and watch the difference in yEd)
149
#$grouproot->setProperties('x' => 778.88, 'y' => 900);
150
151
$d->buildDocument('test');
152
153
Example of using templating (ready to run):
154
155
use strict;
156
use yEd::Document;
157
158
my $d = yEd::Document->new();
159
160
# preparing the templates
161
$d->addNewLabelTemplate('headline', 'NodeLabel', 'The house of Santa Claus', 'positionModell' => 'sandwich-n', 'fontSize' => 20);
162
$d->addNewNodeTemplate('housenode', 'ShapeNode', 'shape' => 'ellipse', 'fillColor' => '#0000ff');
163
my $wall = $d->addNewEdgeTemplate('wall', 'GenericEdge', 'lineWidth' => 5, 'fillColor' => '#0000ff', 'tArrow' => 'standard');
164
$wall->addNewLabel('This', 'positionModell' => 'three_center-scentr', 'textColor' => '#005500', 'backgroundColor' => '#cccccc');
165
$d->addEdgeTemplate('roof', $wall, 'fillColor' => '#ff0000');
166
167
# adding the nodes
168
my $n = $d->addTemplateNode('housenode', 'y' => 300);
169
my $n1 = $d->addTemplateNode('housenode');
170
my $n2 = $d->addTemplateNode('housenode', 'x' => 300);
171
my $n3 = $d->addTemplateNode('housenode', 'x' => 300, 'y' => 300);
172
# adding a node that wasn't defined as a template
173
my $n4 = $d->addNewNode('ShapeNode', 'x' => 150, 'y' => -150, 'shape' => 'triangle', 'fillColor' => '#ff0000');
174
$n4->addLabel($d->getTemplateLabel('headline'));
175
176
# adding the edges
177
$d->addTemplateEdge('wall',$n,$n1);
178
($d->addTemplateEdge('wall',$n1,$n2)->labels())[0]->text('is');
179
($d->addTemplateEdge('wall',$n2,$n)->labels())[0]->text('the');
180
($d->addTemplateEdge('wall',$n,$n3)->labels())[0]->text('house');
181
($d->addTemplateEdge('wall',$n3,$n2)->labels())[0]->text('of');
182
($d->addTemplateEdge('roof',$n2,$n4)->labels())[0]->text('San-');
183
($d->addTemplateEdge('roof',$n4,$n1)->labels())[0]->text('ta');
184
($d->addTemplateEdge('wall',$n1,$n3)->labels())[0]->text('Claus');
185
186
$d->buildDocument('santa_claus');
187
188
=head1 OBJECT IDENTIFIERS
189
190
All Nodes and Edges of a Document must have an unique ID.
191
192
If you only use the yEd::Document's C functions and templating features you won't have to care for IDs.
193
194
But if you need to externally create Nodes or Edges from its classes you will have to provide the ID yourself.
195
196
In this case it is up to you to ensure unique values unless you obtain the IDs via yEd::Document's C function.
197
198
This automatically created IDs will always be positive integer values, but if you provide your own IDs you can use almost anything.
199
200
If you load and save a Document in yEd all IDs will be converted to yEd's syntax ('n0','n1',...,'e0','e1',...).
201
202
=head1 TEMPLATING
203
204
If you have to add many copies of the same modification of a Node, Edge or Label have a look at yEd::Document's template functions (each with a 'Template' in its name).
205
206
These offer the ability to save an object's configuration and return and/or add copies of it.
207
208
For Nodes and Edges added Labels will be copied along with the template.
209
210
For Nodes that are relative to other Nodes you may want to call C or C on the copy (or provide it with the properties parameter).
211
212
For Edges all waypoints are copied along with the template, this may not be what you want unless the Edge's C property is set, consider calling C on the copy.
213
214
Have a look at the 'Example of using templating' in the section L.
215
216
=head1 RELATIVE OBJECTS
217
218
A Node or Edge-waypoint by default will be absolute, this is its x and y values are directly applied to the drawing pane.
219
220
However you can change a Node to be relative to a given other Node by using the Node's C property.
221
In this case x and y are values that are added to the other Node's absolute position to determine the Node's position.
222
The other Node however may be relative to yet another Node, too.
223
Just make sure you don't create dependency loops. ;)
224
225
If you manually cut the relation of two Nodes by setting C on the dependend Node, the provided C and C value will not be changed but only interpreted as being absolute further on.
226
If you want to cut the relation without changing the Node's current absolute position call C<$n-Ex($n-EabsX())> and C<$n-Ey($n-EabsY())> BEFORE the call to C<$n-Erelative(0)>.
227
228
Note that the C and C coords of a Node refer to its upper left corner or rather the upper left corner of the Node's surrounding rectangle (e.g. for ellipse ShapeNodes).
229
Consider this if you do any Node positioning math in your scripts.
230
231
Edges do have such a property, too, it is called C but is a boolean value in contrast to Node's property.
232
If set to a true value any waypoints added to this Edge will be positioned relative to the previous waypoint.
233
The first waypoint's position will be calculated relative to the Edge's anchor point on its C Node (C,C).
234
235
If you change from C to C there will be no conversion of the waypoint coords by default (same behavior as for Nodes).
236
237
Note that the source Node anchor point (C,C) is relative to the Node's center (C = C = 0) (as is true for the C anchor).
238
This is somehow inconsistent as the Node's coords don't describe its center (you can obtain a Node's center by using its C function).
239
Consider this if you do any positioning math in your scripts.
240
However for relative waypoints you will likely have the desired bahavior by default, since first waypoint is relative to the anchor point which's absolute position is automatically computed for you by this package.
241
242
Have a look at the 'Example of using relative entities and layers' in the section L.
243
244
Tip: If you create "groups" with the reative feature always build your group on top of a root node that spans the whole area of the group.
245
If you need the Nodes to not be surrounded by a background root Node simply make it invisible (no fill color, no border).
246
This way you can always ask your root Node for the C and C of the whole "group" if you need these values for calculation. :)
247
248
Note that this way of "grouping" has no effect in yEd, it does only exist within the code.
249
250
=cut
251
252
#TODO: VirtualGroupNode Type ? invisible rectangle ShapeNode that allows adding relative Nodes and autosizes itself on Node addition/removal .. ?.
253
254
=head1 LAYERS
255
256
yEd Documents / Nodes do only support "virtual" layering which is saved to the graphml file by giving the Nodes a special order (first Node defined is drawn first).
257
258
This concept has been adopted by this package.
259
The layer is described as a property of each Node, named C (Edges do not support layering, they will always be drawn on top the Nodes).
260
Its default value is 0 which is the bottom layer.
261
You can define the layer of a Node as any positive integer value, where a higher value is "more in front".
262
263
Within a single layer the drawing order of Nodes is undefined, if they overlap or need a special order because of other reasons use different layers.
264
265
Like with any other property you may obtain a list of all Nodes on a specified layer by using C (e.g. C 3>).
266
267
Have a look at the 'Example of using relative entities and layers' in the section L.
268
269
=head1 SUBROUTINES/METHODS
270
271
=head2 new
272
273
Creates a new instance of yEd::Document and initializes it.
274
275
=head3 EXAMPLE
276
277
my $doc = yEd::Document->new();
278
279
=cut
280
281
sub new {
282
my $self = {};
283
bless $self, shift;
284
$self->resetDocument();
285
$self->{'dummynode'} = yEd::Node::ShapeNode->new('noid');
286
return $self;
287
}
288
289
my %graphmlAttr = (
290
'xmlns'=> 'http://graphml.graphdrawing.org/xmlns',
291
'xmlns:xsi'=> 'http://www.w3.org/2001/XMLSchema-instance',
292
'xmlns:y' => 'http://www.yworks.com/xml/graphml',
293
'xmlns:yed' => 'http://www.yworks.com/xml/yed/3',
294
'xsi:schemaLocation' => 'http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd',
295
);
296
my %docKeyAttr = (
297
'd0' => { 'for' => 'graph', 'attr.type' => 'string', 'attr.name' => 'Description' },
298
'd1' => { 'for' => 'port', 'yfiles.type' => 'portgraphics' },
299
'd2' => { 'for' => 'port', 'yfiles.type' => 'portgeometry' },
300
301
'd4' => { 'for' => 'node', 'attr.type' => 'string', 'attr.name' => 'url' },
302
'd5' => { 'for' => 'node', 'attr.type' => 'string', 'attr.name' => 'description' },
303
'd6' => { 'for' => 'node', 'yfiles.type' => 'nodegraphics' },
304
'd7' => { 'for' => 'graphml', 'yfiles.type' => 'resources' },
305
'd8' => { 'for' => 'edge', 'attr.type' => 'string', 'attr.name' => 'url' },
306
'd9' => { 'for' => 'edge', 'attr.type' => 'string', 'attr.name' => 'description' },
307
'd10' => { 'for' => 'edge', 'yfiles.type' => 'edgegraphics' },
308
);
309
310
=head2 resetDocument
311
312
Resets (empties) and reinitializes this Document.
313
314
Previously registered templates will be kept.
315
316
=head3 EXAMPLE
317
318
$doc->resetDocument();
319
320
=cut
321
322
sub resetDocument {
323
my $self = shift;
324
$self->{'DOC'} = XML::LibXML::Document->new('1.0', 'UTF-8');
325
$self->{'DOC'}->setStandalone(0);
326
$self->{'ROOT'} = $self->{'DOC'}->createElement('graphml');
327
foreach my $key (keys %graphmlAttr) {
328
$self->{'ROOT'}->setAttribute($key => $graphmlAttr{$key});
329
}
330
foreach my $id (keys %docKeyAttr) {
331
my $keyelement = $self->{'ROOT'}->addNewChild('', 'key');
332
$keyelement->setAttribute('id' => $id);
333
foreach my $key (keys %{$docKeyAttr{$id}}) {
334
$keyelement->setAttribute($key => $docKeyAttr{$id}{$key});
335
}
336
}
337
$self->{'nodes'} = {};
338
$self->{'edges'} = {};
339
$self->{'lastid'} = 0;
340
return;
341
}
342
343
=head2 buildDocument
344
345
Builds the Document and returns it as a string.
346
347
If a filename is provided (without the ending) it will additionally be written into filename.graphml .
348
349
=head3 EXAMPLE
350
351
my $xml = $doc->buildDocument();
352
my $xml = $doc->buildDocument('/path/to/mydoc');
353
354
=cut
355
356
sub buildDocument {
357
my $self = shift;
358
my $filename = shift;
359
my $graph = $self->{'ROOT'}->addNewChild('', 'graph');
360
$graph->setAttribute('edgedefault' => 'directed');
361
$graph->setAttribute('id' => 'G');
362
$graph->addNewChild('', 'data')->setAttribute('key' => 'd0');
363
for (my $l = 0; $l <= $self->getFrontLayer(); $l++) {
364
foreach my $n ($self->getNodesByProperties('layer' => $l)) {
365
if (my $n2 = $n->relative()) {
366
confess "node $n2 (id: " . $n2->id() . ") as referenced by relative node $n (id: " . $n->id() . ") is not part of this document" unless ($self->hasNodeId($n2->id()));
367
}
368
$graph->addChild($n->_build());
369
}
370
}
371
foreach my $e ($self->getEdges()) {
372
foreach my $n ($e->getNodes()) {
373
confess "node $n (id: " . $n->id() . ") as referenced by edge $e (id: " . $e->id() . ") is not part of this document" unless ($self->hasNodeId($n->id()));
374
}
375
$graph->addChild($e->_build());
376
}
377
my $datablock = $self->{'ROOT'}->addNewChild('', 'data');
378
$datablock->setAttribute('key' => 'd7');
379
$datablock->addNewChild('', 'y:Resources');
380
$self->{'DOC'}->setDocumentElement($self->{'ROOT'});
381
my $out = $self->{'DOC'}->toString();
382
if ($filename) {
383
$filename .= '.graphml';
384
open( OUTFILE, '>', $filename ) or confess "couldn't open $filename for write access";
385
print OUTFILE $out;
386
close OUTFILE;
387
}
388
return $out;
389
}
390
391
=head2 getFreeId
392
393
Returns the next still free C, that can be used for creating new Nodes or Edges.
394
395
This is only required if you externally create new Nodes/Edges and you could also use own values as IDs (you will then have to ensure that all IDs are unique).
396
397
=head3 EXAMPLE
398
399
my $myid = $doc->getFreeId();
400
401
=cut
402
403
sub getFreeId {
404
my $self = shift;
405
my $id = $self->{'lastid'};
406
do {
407
$id++;
408
} while ($self->hasNodeId($id) or $self->hasEdgeId($id));
409
$self->{'lastid'} = $id;
410
return $id;
411
}
412
413
=head2 getFrontLayer
414
415
Returns the highest C that is set for any Node of this Document.
416
417
=head3 EXAMPLE
418
419
my $front = $doc->getFrontLayer();
420
421
=cut
422
423
sub getFrontLayer {
424
my $self = shift;
425
my $layer = 0;
426
foreach my $node ($self->getNodes()) {
427
$layer = $node->layer() if ($node->layer() > $layer);
428
}
429
return $layer;
430
}
431
432
# Templates
433
434
=head2 addNewLabelTemplate
435
436
Creates a template for Labels.
437
438
Must have parameters are: templatename (a string for accessing the template) , labeltype (Node- or EdgeLabel) , labeltext (the C for the Label)
439
440
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
441
442
The template Label is returned as a reference, if you modify this reference all further copies of the template will be affected !
443
Copies created earlier will not be affected since they are as said copies.
444
445
=head3 EXAMPLE
446
447
my $tmplLabel = $doc->addNewLabelTemplate('mytemplate', 'NodeLabel', 'hello world', 'alignment' => 'left');
448
449
=cut
450
451
sub addNewLabelTemplate {
452
my ($self, $name, $type, @param) = @_;
453
confess 'provide templateName, labelType, labelText, [labelProperties]' unless (defined $name and defined $type and @param);
454
$type = 'yEd::Label::' . $type;
455
my $o = $type->new(@param);
456
return $self->addLabelTemplate($name, $o);
457
}
458
459
=head2 addLabelTemplate
460
461
Creates a template for Labels.
462
463
Must have parameters are: templatename (a string for accessing the template) , label (a reference to a Label object)
464
465
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
466
467
The source Label will not be changed, a copy of the Label will be used as template.
468
If you want to modify the template after adding it use the returned reference to the copy.
469
470
=head3 EXAMPLE
471
472
my $tmplLabel = $doc->addLabelTemplate('mytemplate', $srcLabel, 'text' => 'foo');
473
474
=cut
475
476
sub addLabelTemplate {
477
my ($self, $name, $label,@param) = @_;
478
confess 'must be of type yEd::Label' unless ($label->isa('yEd::Label'));
479
$self->{'labelTemplates'}{$name} = $label->copy(@param);
480
return $self->{'labelTemplates'}{$name};
481
}
482
483
=head2 getTemplateLabel
484
485
Creates a copy of a previously created Label template and returns it.
486
487
Must have parameters are: templatename (a string for accessing the template)
488
489
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
490
491
In contrast to Nodes and Edges Label templates can not be added directly because the need to be added to a Node or Edge.
492
So use this function to obtain a copy and add it somewhere yourself.
493
494
=head3 EXAMPLE
495
496
my $label = $doc->getTemplateLabel('mytemplate', 'text' => 'bar');
497
498
=cut
499
500
sub getTemplateLabel {
501
my ($self, $name, @params) = @_;
502
confess 'provide templateName, [labelProperties]' unless (defined $name);
503
confess "no such template: $name" unless (exists $self->{'labelTemplates'}{$name});
504
return $self->{'labelTemplates'}{$name}->copy(@params);
505
}
506
507
=head2 addNewNodeTemplate
508
509
Creates a template for Nodes.
510
511
Must have parameters are: templatename (a string for accessing the template) , nodetype (Shape- or GenericNode)
512
513
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
514
515
The template Node is returned as a reference, if you modify this reference all further copies of the template will be affected !
516
Copies created earlier will not be affected since they are as said copies.
517
518
=head3 EXAMPLE
519
520
my $tmplNode = $doc->addNewNodeTemplate('mytemplate', 'ShapeNode', 'width' => 300, 'height' => 200);
521
522
=cut
523
524
sub addNewNodeTemplate {
525
my ($self, $name, $type, @param) = @_;
526
confess 'provide templateName, nodeType, [nodeProperties]' unless (defined $name and defined $type);
527
$type = 'yEd::Node::' . $type;
528
my $o = $type->new('noid', @param);
529
return $self->addNodeTemplate($name, $o);;
530
}
531
532
=head2 addNodeTemplate
533
534
Creates a template for Nodes.
535
536
Must have parameters are: templatename (a string for accessing the template) , node (a reference to a Node object)
537
538
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
539
540
The source Node will not be changed, a copy of the Node will be used as template.
541
If you want to modify the template after adding it use the returned reference to the copy.
542
543
=head3 EXAMPLE
544
545
my $tmplNode = $doc->addNodeTemplate('mytemplate', $srcNode, 'width' => 300, 'height' => 200);
546
547
=cut
548
549
sub addNodeTemplate {
550
my ($self, $name, $node, @param) = @_;
551
confess 'must be of type yEd::Node' unless ($node->isa('yEd::Node'));
552
$self->{'nodeTemplates'}{$name} = $node->copy('noid', @param);
553
return $self->{'nodeTemplates'}{$name};
554
}
555
556
=head2 getTemplateNode
557
558
Creates a copy of a previously created Node template and returns it.
559
560
Must have parameters are: templatename (a string for accessing the template)
561
562
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
563
564
=head3 EXAMPLE
565
566
my $node = $doc->getTemplateNode('mytemplate', 'x' => 50, 'y' => 200);
567
568
=cut
569
570
sub getTemplateNode {
571
my ($self, $name, @params) = @_;
572
confess 'provide templateName, [nodeProperties]' unless (defined $name);
573
confess "no such template: $name" unless (exists $self->{'nodeTemplates'}{$name});
574
return $self->{'nodeTemplates'}{$name}->copy($self->getFreeId(), @params);
575
}
576
577
=head2 addTemplateNode
578
579
Creates a copy of a previously created Node template, adds it to the Document and returns it.
580
581
Must have parameters are: templatename (a string for accessing the template)
582
583
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
584
585
=head3 EXAMPLE
586
587
my $node = $doc->addTemplateNode('mytemplate', 'x' => 50, 'y' => 200);
588
589
=cut
590
591
sub addTemplateNode {
592
my ($self, $name, @params) = @_;
593
my $o = $self->getTemplateNode($name, @params);
594
$self->addNode($o);
595
return $o;
596
}
597
598
=head2 addNewEdgeTemplate
599
600
Creates a template for Edges.
601
602
Must have parameters are: templatename (a string for accessing the template) , edgetype (one of the supported Edge class names)
603
604
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
605
606
The template Edge is returned as a reference, if you modify this reference all further copies of the template will be affected !
607
Copies created earlier will not be affected since they are as said copies.
608
609
=head3 EXAMPLE
610
611
my $tmplEdge = $doc->addNewEdgeTemplate('mytemplate', 'ArcEdge', 'tArrow' => 'standard');
612
613
=cut
614
615
sub addNewEdgeTemplate {
616
my ($self, $name, $type, @param) = @_;
617
confess 'provide templateName, edgeType, [edgeProperties]' unless (defined $name and defined $type);
618
$type = 'yEd::Edge::' . $type;
619
my $o = $type->new('noid', $self->{'dummynode'}, $self->{'dummynode'}, @param);
620
return $self->addEdgeTemplate($name, $o);;
621
}
622
623
=head2 addEdgeTemplate
624
625
Creates a template for Edges.
626
627
Must have parameters are: templatename (a string for accessing the template) , edge (a reference to an Edge object)
628
629
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
630
631
The source Edge will not be changed, a copy of the Edge will be used as template.
632
If you want to modify the template after adding it use the returned reference to the copy.
633
634
=head3 EXAMPLE
635
636
my $tmplEdge = $doc->addEdgeTemplate('mytemplate', $srcEdge, 'tArrow' => 'standard');
637
638
=cut
639
640
sub addEdgeTemplate {
641
my ($self, $name, $edge, @param) = @_;
642
confess 'must be of type yEd::Edge' unless ($edge->isa('yEd::Edge'));
643
$self->{'edgeTemplates'}{$name} = $edge->copy('noid', $self->{'dummynode'}, $self->{'dummynode'}, @param);
644
return $self->{'edgeTemplates'}{$name};
645
}
646
647
=head2 getTemplateEdge
648
649
Creates a copy of a previously created Edge template and returns it.
650
651
Must have parameters are: templatename (a string for accessing the template), source, target (the new C and C L refs for the new Edge)
652
653
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
654
655
=head3 EXAMPLE
656
657
my $edge = $doc->getTemplateEdge('mytemplate', $srcNode, $trgNode, 'tArrow' => 'standard');
658
659
=cut
660
661
sub getTemplateEdge {
662
my ($self, $name, $s, $t, @params) = @_;
663
confess 'provide templateName, edgeSourceNode, edgeTargetNode, [edgeProperties]' unless (defined $name and defined $s and defined $t);
664
confess "no such template: $name" unless (exists $self->{'edgeTemplates'}{$name});
665
return $self->{'edgeTemplates'}{$name}->copy($self->getFreeId(), $s, $t, @params);
666
}
667
668
=head2 addTemplateEdge
669
670
Creates a copy of a previously created Edge template, adds it to the Document and returns it.
671
672
Must have parameters are: templatename (a string for accessing the template), source, target (the new C and C L refs for the new Edge)
673
674
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
675
676
=head3 EXAMPLE
677
678
my $edge = $doc->addTemplateEdge('mytemplate', $srcNode, $trgNode, 'tArrow' => 'standard');
679
680
=cut
681
682
sub addTemplateEdge {
683
my ($self, $name, $s, $t, @params) = @_;
684
my $o = $self->getTemplateEdge($name, $s, $t, @params);
685
$self->addEdge($o);
686
return $o;
687
}
688
689
# Nodes
690
691
=head2 addNewNode
692
693
Creates a new Node, adds it and returns a reference to it.
694
695
Must have parameters are: nodetype (Shape- or GenericNode)
696
697
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
698
699
=head3 EXAMPLE
700
701
my $node = $doc->addNewNode('ShapeNode', 'x' => 60, 'y' => 450);
702
703
=cut
704
705
sub addNewNode {
706
my ($self, $type, @param) = @_;
707
$type = 'yEd::Node::' . $type;
708
my $node = $type->new($self->getFreeId(), @param);
709
$self->addNode($node);
710
return $node;
711
}
712
713
=head2 addNode
714
715
Takes a L as parameter and adds it to the Document.
716
717
=head3 EXAMPLE
718
719
$doc->addNode($node);
720
721
=cut
722
723
sub addNode {
724
my ($self, $node) = @_;
725
confess 'must be of type yEd::Node' unless ($node->isa('yEd::Node'));
726
confess "node ids must be unique: " . $node->id() if $self->hasNodeId($node->id());
727
$self->{'nodes'}{$node->id()} = $node;
728
return;
729
}
730
731
=head2 getNodes
732
733
Returns an array of all Nodes of this Document.
734
735
=head3 EXAMPLE
736
737
my @nodes = $doc->getNodes();
738
739
=cut
740
741
sub getNodes {
742
return values %{$_[0]->{'nodes'}};
743
}
744
745
=head2 getNodesByProperties
746
747
Takes arguments of the form C value, property2 =E value2, ...>.
748
749
Returns a list of all Nodes that matches the given filter.
750
751
=head3 EXAMPLE
752
753
my @nodes = $doc->getNodesByProperties('width' => 300);
754
755
=cut
756
757
sub getNodesByProperties {
758
my ($self, @properties) = @_;
759
my @nodes;
760
foreach my $node ($self->getNodes()) {
761
push @nodes, $node if $node->hasProperties(@properties);
762
}
763
return @nodes;
764
}
765
766
=head2 hasNodeId
767
768
Takes a Node's C as parameter and returns true if it is present in this Document.
769
770
=head3 EXAMPLE
771
772
if ($doc->hasNodeId('myid1')) { ...
773
774
=cut
775
776
sub hasNodeId {
777
my ($self, $id) = @_;
778
confess 'no id provided' unless (defined $id);
779
return exists $self->{'nodes'}{$id};
780
}
781
782
=head2 getNodeById
783
784
Takes an C as parameter and returns the Node with this C, if present in this Document.
785
786
=head3 EXAMPLE
787
788
my $node = $doc->getNodeById('myid1');
789
790
=cut
791
792
sub getNodeById {
793
my ($self, $id) = @_;
794
confess 'no id provided' unless (defined $id);
795
return $self->{'nodes'}{$id};
796
}
797
798
=head2 removeNode
799
800
Takes a L as parameter and tries to remove it from this document.
801
802
All connected Edges will be removed, too.
803
804
If there are Nodes C to the given Node, they will also be removed (and their Edges and Nodes relative to these Nodes and so on, until all dependencies are resolved) unless the second (keepRelative) parameter is true.
805
In this case the absolute C and C values are calculated and the relationship is cut, making the orphaned Nodes absolute.
806
807
=head3 EXAMPLE
808
809
# in the "Example of using relative entities and layers" (see SYNOPSIS)
810
# only the cirlce will remain if you call
811
$d->removeNode($grouproot);
812
# only $grouproot and $e will be removed if you call
813
$d->removeNode($grouproot, 1);
814
# $n will then be absolute at its last position
815
816
=cut
817
818
sub removeNode {
819
my ($self, $node, $keepRelative) = @_;
820
confess 'no yEd::Node provided' unless (defined $node and $node->isa('yEd::Node'));
821
if ($self->hasNodeId($node->id())) {
822
delete $self->{'nodes'}{$node->id()};
823
foreach my $e ($node->getEdges()) {
824
$self->removeEdge($e);
825
}
826
foreach my $n ($self->getNodesByProperties('relative' => $node)) {
827
if ($keepRelative) {
828
$n->x($n->absX());
829
$n->y($n->absY());
830
$n->relative(0);
831
} else {
832
$self->removeNode($n);
833
}
834
}
835
}
836
return;
837
}
838
839
# Edges
840
841
=head2 addNewEdge
842
843
Creates a new Edge, adds it and returns a reference to it.
844
845
Must have parameters are: edgetype (one of the supported Edge class names), source, target (the C and C L refs for the new Edge)
846
847
Further parameters to set properties are optional (C value, property2 =E value2, ...>).
848
849
=head3 EXAMPLE
850
851
my $edge = $doc->addNewEdge('ArcEdge', $srcNode, $trgNode, 'tArrow' => 'standard');
852
853
=cut
854
855
sub addNewEdge {
856
my ($self, $type, @param) = @_;
857
$type = 'yEd::Edge::' . $type;
858
my $edge = $type->new($self->getFreeId(), @param);
859
$self->addEdge($edge);
860
return $edge;
861
}
862
863
=head2 addEdge
864
865
Takes a L as parameter and adds it to the Document.
866
867
=head3 EXAMPLE
868
869
$doc->addEdge($edge);
870
871
=cut
872
873
sub addEdge {
874
my ($self, $edge) = @_;
875
confess 'must be of type yEd::Edge' unless ($edge->isa('yEd::Edge'));
876
confess 'edge ids must be unique: ' . $edge->id() if $self->hasEdgeId($edge->id());
877
$self->{'edges'}{$edge->id()} = $edge;
878
return;
879
}
880
881
=head2 getEdges
882
883
Returns an array of all Edges of this Document.
884
885
=head3 EXAMPLE
886
887
my @edges = $doc->getEdges();
888
889
=cut
890
891
sub getEdges {
892
return values %{$_[0]->{'edges'}};
893
}
894
895
=head2 getEdgesByProperties
896
897
Takes arguments of the form C value, property2 =E value2, ...>.
898
899
Returns a list of all Edges that matches the given filter.
900
901
=head3 EXAMPLE
902
903
my @edges = $doc->getEdgesByProperties('tArrow' => 'standard');
904
905
=cut
906
907
sub getEdgesByProperties {
908
my ($self, @properties) = @_;
909
my @edges;
910
foreach my $edge ($self->getEdges()) {
911
push @edges, $edge if $edge->hasProperties(@properties);
912
}
913
return @edges;
914
}
915
916
=head2 hasEdgeId
917
918
Takes an Edge's C as parameter and returns true if it is present in this Document.
919
920
=head3 EXAMPLE
921
922
if ($doc->hasEdgeId('myid1')) { ...
923
924
=cut
925
926
sub hasEdgeId {
927
my ($self, $id) = @_;
928
confess 'no id provided' unless (defined $id);
929
return exists $self->{'edges'}{$id};
930
}
931
932
=head2 getEdgeById
933
934
Takes an C as parameter and returns the Edge with this C, if present in this Document.
935
936
=head3 EXAMPLE
937
938
my $edge = $doc->getEdgeById('myid1');
939
940
=cut
941
942
sub getEdgeById {
943
my ($self, $id) = @_;
944
confess 'no id provided' unless (defined $id);
945
return $self->{'edges'}{$id};
946
}
947
948
=head2 removeEdge
949
950
Takes a L as parameter and tries to remove it from this document.
951
952
=head3 EXAMPLE
953
954
$doc->removeEdge($edge);
955
956
=cut
957
958
sub removeEdge {
959
my ($self, $edge) = @_;
960
confess 'no yEd::Edge provided' unless (defined $edge and $edge->isa('yEd::Edge'));
961
if ($self->hasEdgeId($edge->id())) {
962
$edge->_deregisterAll();
963
delete $self->{'edges'}{$edge->id()};
964
}
965
return;
966
}
967
968
=head1 AUTHOR
969
970
Heiko Finzel, C<< >>
971
972
=head1 BUGS
973
974
Please report any bugs or feature requests to C, or through
975
the web interface at L. I will be notified, and then you'll
976
automatically be notified of progress on your bug as I make changes.
977
978
=head1 SUPPORT
979
980
You can find documentation for this module with the perldoc command.
981
982
perldoc yEd::Document
983
984
Also see perldoc of the other classes of this package.
985
986
You can also look for information at:
987
988
=over 4
989
990
=item * RT: CPAN's request tracker (report bugs here)
991
992
L
993
994
=item * AnnoCPAN: Annotated CPAN documentation
995
996
L
997
998
=item * CPAN Ratings
999
1000
L
1001
1002
=item * Search CPAN
1003
1004
L
1005
1006
=back
1007
1008
=head1 LICENSE AND COPYRIGHT
1009
1010
Copyright 2014 Heiko Finzel.
1011
1012
This program is free software; you can redistribute it and/or modify it
1013
under the terms of the the Artistic License (2.0). You may obtain a
1014
copy of the full license at:
1015
1016
L
1017
1018
Any use, modification, and distribution of the Standard or Modified
1019
Versions is governed by this Artistic License. By using, modifying or
1020
distributing the Package, you accept this license. Do not use, modify,
1021
or distribute the Package, if you do not accept this license.
1022
1023
If your Modified Version has been derived from a Modified Version made
1024
by someone other than you, you are nevertheless required to ensure that
1025
your Modified Version complies with the requirements of this license.
1026
1027
This license does not grant you the right to use any trademark, service
1028
mark, tradename, or logo of the Copyright Holder.
1029
1030
This license includes the non-exclusive, worldwide, free-of-charge
1031
patent license to make, have made, use, offer to sell, sell, import and
1032
otherwise transfer the Package with respect to any patent claims
1033
licensable by the Copyright Holder that are necessarily infringed by the
1034
Package. If you institute patent litigation (including a cross-claim or
1035
counterclaim) against any party alleging that the Package constitutes
1036
direct or contributory patent infringement, then this Artistic License
1037
to you shall terminate on the date that such litigation is filed.
1038
1039
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
1040
AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
1041
THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
1042
PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
1043
YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
1044
CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
1045
CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
1046
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1047
1048
=cut
1049
1050
1;