File Coverage

blib/lib/Chart/Sequence.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Chart::Sequence;
2              
3             $VERSION = 0.002;
4              
5             =head1 NAME
6              
7             Chart::Sequence - A sequence class
8              
9             =head1 SYNOPSIS
10              
11             use Chart::Sequence;
12             my $s = Chart::Sequence->new(
13             Nodes => [qw( A B C )],
14             Messages => [
15             [ A => B => "Message 1" ],
16             [ B => A => "Ack 1" ],
17             [ B => C => "Message 2" ],
18             ],
19             );
20              
21             # or #
22             my $s = Chart::Sequence->new(
23             SeqMLInput => "foo.seqml",
24             );
25              
26              
27             my $r = Chart::Sequence::Imager->new;
28             my $png => $r->render( $s => "png" );
29             $r->render_to_file( $s => "foo.png" );
30              
31             =head1 DESCRIPTION
32              
33             ALPHA CODE ALERT: This is alpha code and the API will be
34             changing. For instance, I need to generalize "messages"
35             in to "events". Feedback wanted.
36              
37             A sequence chart portrays a sequence of events occuring among
38             a set of objects or, as we refer to them, nodes.
39              
40             So far, this class only supports portrayal of messages between
41             nodes (and then not even from a node to itself). More events
42             are planned.
43              
44             So, a Chart::Sequence has a list of nodes and a list of messages.
45             Nodes will be instantiated automatically from the messages destinations
46             (an option to disable this will be forthcoming).
47              
48             A sequence may created and populated in one or more of 3 ways:
49              
50             =over
51              
52             =item 1
53              
54             Messages and, optionally, nodes may be passed in to new()
55              
56             =item 2
57              
58             Messages and, optionally, nodes may be added or removed en masse
59             using the appropriate methods.
60              
61             =item 3
62              
63             "SeqML" files may be parsed using Chart::Sequence::SAXBuilder.
64             See the test scripts for examples
65              
66             =back
67              
68             A small example (example_sequence_chart.png) is included in the
69             tarball.
70              
71             Once built, charts may be layed out using a pluggable layout and
72             rendering system for which only one pixel-graphics oriented layout
73             (L) and one renderer
74             (L) exist.
75              
76             More docs forthcoming; feel free to ask.
77              
78             =cut
79              
80             require Chart::Sequence::Object;
81              
82             @ISA = qw( Chart::Sequence::Object );
83              
84 3     3   11466 use strict;
  3         6  
  3         95  
85 3     3   1537 use Chart::Sequence::Node ();
  0            
  0            
86             use Chart::Sequence::Message ();
87              
88             =head1 METHODS
89              
90             =over
91              
92             =cut
93              
94             =item new
95              
96             my $s = Chart::Sequence->new;
97              
98             =cut
99              
100             sub new {
101             my $proto = shift;
102             my $seqml;
103              
104             for ( my $i = 0; $i <= $#_; $i += 2 ) {
105             if ( $_[$i] eq "SeqML" ) {
106             ( undef, $seqml ) = splice @_, $i, 2;
107             last;
108             }
109             }
110              
111             my $self = $proto->SUPER::new( @_ );
112              
113             $self->read_seqml( $seqml ) if defined $seqml;
114              
115             return $self;
116             }
117              
118             __PACKAGE__->make_methods((
119             '@nodes', => {
120             set_pre => <<'END_SET_PRE',
121             $_ = Chart::Sequence::Node->new( $_ )
122             unless UNIVERSAL::isa( $_, "Chart::Sequence::Node" );
123             $_->number( int @{$self->{Nodes}} );
124             END_SET_PRE
125              
126             get_pre => <<'END_GET_POST',
127             my %seen = map { ( $_->name => 1 ) } @{$self->{Nodes}};
128             $self->push_nodes(
129             map {
130             $seen{$_}++ ? ()
131             : Chart::Sequence::Node->new( Name => $_ );
132             }
133             map { ( $_->from, $_->to ) } $self->messages
134             );
135             END_GET_POST
136              
137             },
138              
139             '@messages' => {
140             set_pre => <<'END_SET_PRE',
141             $_ = Chart::Sequence::Message->new( $_ )
142             unless UNIVERSAL::isa( $_, "Chart::Sequence::Message" );
143             $_->number( int $self->messages );
144             END_SET_PRE
145             },
146              
147              
148             '_layout_info',
149             ));
150              
151             =item name
152              
153             Sets/gets the name of this sequence
154              
155             =item nodes
156              
157             A node is something that sends or receives a message.
158              
159             $s->nodes( $node1, $node2, ... );
160             my @nodes = $s->nodes;
161              
162             Sets / gets the list of nodes. If any messages refer to non-existent nodes,
163             the missing nodes are created.
164              
165             =item nodes_ref
166              
167             Sets/gets an ARRAY reference to an array containing nodes.
168              
169             =item push_nodes
170              
171             Appends one or more nodes to the end of the current list.
172              
173             =item node_named
174              
175             Gets a node by name.
176              
177             =cut
178              
179             sub node_named {
180             my $self = shift;
181             my ( $name ) = @_;
182              
183             ## TODO: maintain an index
184             return (grep $_->name eq $name, $self->nodes)[0];
185             }
186              
187             =item messages
188              
189             $s->messages( $msg1, $msg2, ... );
190             my @messages = $s->messages;
191              
192             Returns or sets the list of messages.
193              
194             =item messages_ref
195              
196             Returns or sets the list of messages as an ARRAY reference.
197              
198             =item push_messages
199              
200             Adds messages to the end of the sequence.
201              
202             =cut
203              
204             =item read_seqml
205              
206             my $s = Chart::Sequence->read_seqml( "some.seqml" );
207             $s = Chart::Sequence->read_seqml( "more.seqml" );
208              
209             Reads XML from a filehandle, a SCALAR reference, or a named file.
210              
211             When called as a class method, returns a new Chart::Sequence object.
212             When called as an instance method add additional events to the
213             instance.
214              
215             Requirese the optional prerequisite XML::SAX.
216              
217             =cut
218              
219             sub read_seqml {
220             my $self = shift;
221             my ( $input ) = @_;
222              
223             require XML::SAX::ParserFactory;
224             # TODO: Remove this.
225             $XML::SAX::ParserPackage = "XML::SAX::PurePerl";
226             require Chart::Sequence::SAXBuilder;
227             my $p = XML::SAX::ParserFactory->parser(
228             Handler => Chart::Sequence::SAXBuilder->new(
229             ref $self ? ( Sequence => $self ) : (),
230             ),
231             );
232              
233             my $type = ref $input;
234             if ( $type eq "SCALAR" ) {
235             return $p->parse_string( $$input )->[0];
236             }
237             elsif ( $type eq "GLOB" || UNIVERSAL::isa( $type, "IO::Handle" ) ) {
238             return $p->parse_file( $input )->[0];
239             }
240             elsif ( ! $type ) {
241             return $p->parse_uri( $input )->[0];
242             }
243             else {
244             require Carp;
245             Carp::croak(
246             __PACKAGE__,
247             ": don't know how to read SeqML from a ", $type
248             );
249             }
250             }
251              
252             =back
253              
254             =head1 LIMITATIONS
255              
256             Requires XML::SAX::PurePurl for now. The latest XML::LibXML::SAX
257             seems to not notice
258             declarations.
259              
260             =head1 SEE ALSO
261              
262             L
263              
264             =head1 COPYRIGHT
265              
266             Copyright 2002, R. Barrie Slaymaker, Jr., All Rights Reserved
267              
268             =head1 LICENSE
269              
270             You may use this module under the terms of the BSD, Artistic, oir GPL licenses,
271             any version.
272              
273             =head1 AUTHOR
274              
275             Barrie Slaymaker
276              
277             =cut
278              
279             1;