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