File Coverage

blib/lib/yEd/Document.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


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;