#!/usr/local/bin/perl -w ## ## Copyright 2002 The Regents of the University of California ## All Rights Reserved ## ## Permission to use, copy, modify and distribute any part of this ## CoralReef software package for educational, research and non-profit ## purposes, without fee, and without a written agreement is hereby ## granted, provided that the above copyright notice, this paragraph ## and the following paragraphs appear in all copies. ## ## Those desiring to incorporate this into commercial products or use ## for commercial purposes should contact the Technology Transfer ## Office, University of California, San Diego, 9500 Gilman Drive, La ## Jolla, CA 92093-0910, Ph: (858) 534-5815, FAX: (858) 534-7345. ## ## IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY ## PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL ## DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS ## SOFTWARE, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF ## THE POSSIBILITY OF SUCH DAMAGE. ## ## THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND THE ## UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, ## SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. THE UNIVERSITY ## OF CALIFORNIA MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES ## OF ANY KIND, EITHER IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED ## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A ## PARTICULAR PURPOSE, OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ## ANY PATENT, TRADEMARK OR OTHER RIGHTS. ## ## Report bugs and suggestions to info@caida.org. ## ## This is a first pass at a perl function for writting libsea ## graphs. It is not complete, but is a good starting place. ## ## written by Bradley Huffaker use strict; #################################################################### # Public function which produces a libsea graph based on the arguments # passed to it. # # This function takes a single value which is a pointer to arguments # the user which to use. There exist default values for all arguments. # # Arguments # ----------------------------------------------------------------- # # verbose - if this attribute is found LibSeaGraphWrite will # print extrea information in the graph which helps # the graph's human readablity # # nodes - this should be a pointer to hash which holds all the # attribute value for each node # $nodes{$name}{$attribute} = $value; # links - this should be a pointer to hash which holds all the # attribute value for each link. From and To should # be the same names use in nodes. # $links{"$from\0$to"}{$attribute} = $value; # paths - this should be a pointer to hash which holds all the # attribute value for each path. node names should be the # same as those used in nodes. # $paths{"node1\0node2\0$node2......."}{$attribute} = $value; # # max num str colors - this is the max number of strings which will # get their own color. Above this number colors will be # repeated. If not give it assumes 10 # # filename - this is the filename that the graph will be printed # if this argument is not give it will be printed to # STDOUT # # attributes - pointer to the list of attributes that objects # may have values for. Only attributes listed here will # be included in the final graph. Even if the some objects # have values for them. # title - title of the attribute used to find it in the # objects hash # type - bool, string, int, float # color_type - this creates a new attribute which colors # the nodes by they type: hot_to_cold # # qualifiers - pointer to a list of qualifiers # Each qualifier is a pointer to a hash function with # following values: # type - this provides the type of qualifier # name - name of the qualifier # attributes - list of attributes which are used # bu this qualifier #################################################################### ## convent globals # The max number of unique colors give to strings. my $MAX_NUM_STR_COLORS; # Filahandler pointer which will be printed to. my $filehandle; # If this is defined it prints out extrea words my $verbose; # These store the nodes, links and paths. my %node2value; my %link2value; my %path2value; # This stores the nodes in ordered order my @nodes_ordered; my @links_ordered; my @paths_ordered; # This stores the map between node -> id, link -> id my %node2id; my %link2id; my %attribute2id; sub LibSeaGraphWrite { my ($arguments) = @_; my @arrays = ( $arguments->{"nodes"}, $arguments->{"links"}, $arguments->{"paths"} ); if (defined $arguments->{"max num str colors"}) { $MAX_NUM_STR_COLORS = $arguments->{"MAX_NUM_STR_COLORS"}; } else { $MAX_NUM_STR_COLORS = 10; } foreach my $i (0..$#arrays) { unless (defined $arrays[$i]) { $arrays[$i] = {}; } } my @arguments = ("name","description"); my @values; foreach my $index (0..$#arguments) { my $argument = $arguments[$index]; my $value = $arguments->{$argument}; if (defined $value) { $value =~ s/\"//g; $values[$index] = $value; } } my ($name,$description) = @values; if (defined $arguments->{"filename"}) { my $filename = $arguments->{"filename"}; open(FILEHANDLER, ">$filename") || die("Unalbe to open`$filename' for read:$!"); $filehandle = \*FILEHANDLER; } else { $filehandle = \*STDOUT; } if (defined $arguments->{"verbose"}) { $verbose = 1; } %node2value = %{$arrays[0]}; %link2value = %{$arrays[1]}; %path2value = %{$arrays[2]}; ## Set up also fills in the arrays above my ($num_nodes, $num_links, $num_paths, $num_pathLinks) = SetUp(); print $filehandle "Graph\n"; print $filehandle "{\n"; print $filehandle " \@name=" if (defined $verbose); print $filehandle "\"$name\";\n"; print $filehandle " \@description=" if (defined $verbose); print $filehandle "\"$description\";\n"; print $filehandle " \@numNodes=" if (defined $verbose); print $filehandle "$num_nodes;\n"; print $filehandle " \@numLinks=" if (defined $verbose); print $filehandle "$num_links;\n"; print $filehandle " \@numPaths=" if (defined $verbose); print $filehandle "$num_paths;\n"; print $filehandle " \@numPathLinks=" if (defined $verbose); print $filehandle "$num_pathLinks;\n"; print_links(); print_paths(); print $filehandle " \@enumerations=" if (defined $verbose); print $filehandle ";\n"; print $filehandle " \@attributeDefinitions=" if defined ($verbose); print $filehandle "[\n"; if (defined $arguments->{attributes}) { my @attributes = @{$arguments->{attributes}}; my $id = 0; foreach my $index ( 0..$#attributes) { my ($title, $type, $color_type, $default) = ( $attributes[$index]->{title}, $attributes[$index]->{type}, $attributes[$index]->{color_type}, $attributes[$index]->{default} ); if ($index > 0) { print $filehandle ",\n"; } $attribute2id{$title} = $id++; print_attribute($title, $type, $default); if (defined $color_type) { $id++; print $filehandle ",\n"; print_attribute_to_color ($color_type, $title, $type); } } } print $filehandle "\n"; print $filehandle " " if (defined $verbose); print $filehandle "];\n"; print $filehandle " \@qualifiers=" if (defined $verbose); print $filehandle "[\n"; if (defined $arguments->{qualifiers}) { foreach my $i (0..$#{$arguments->{qualifiers}}) { my $qualifier = $arguments->{qualifiers}->[$i]; print_qualifier($qualifier->{type}, $qualifier->{name}, $qualifier->{attributes}); if ($i > 0) { print $filehandle ",\n"; } else { print $filehandle "\n"; } } print $filehandle "\n"; } print $filehandle " " if (defined $verbose); print $filehandle "];\n"; print $filehandle " \@filters=" if defined ($verbose); print $filehandle ";\n"; print $filehandle " \@selectors=" if defined ($verbose); print $filehandle ";\n"; print $filehandle " \@displays=" if defined ($verbose); print $filehandle ";\n"; print $filehandle " \@presentations=" if defined ($verbose); print $filehandle ";\n"; print $filehandle " \@presentationMenus=" if defined ($verbose); print $filehandle ";\n"; print $filehandle " \@displayMenus=" if defined ($verbose); print $filehandle ";\n"; print $filehandle " \@selectorMenus=" if defined ($verbose); print $filehandle ";\n"; print $filehandle " \@filterMenus=" if defined ($verbose); print $filehandle ";\n"; print $filehandle " \@attributeMenus=" if defined ($verbose); print $filehandle ";\n"; print $filehandle "}\n"; close $filehandle; } ############################################################################ sub SetUp { my $num_pathLinks = GetNumPathLinks(); my %nodes; my %links; my $num_nodes = @nodes_ordered = keys %node2value; my $num_links = @links_ordered = keys %link2value; my $num_paths = @paths_ordered = keys %path2value; foreach my $node (@nodes_ordered) { $nodes{$node} = 1; } foreach my $link (@links_ordered) { my ($from, $to) = split /\0/, $link; $nodes{$from} = 1; $nodes{$to} = 1; $links{$link} = 1; } my $num_pathLinks = 0; foreach my $path (@paths_ordered) { my $last_node; my @nodes = split /\0/, $path; $num_pathLinks += $#nodes; foreach my $node (@nodes) { $nodes{$node} = 1; if (defined $last_node) { my $link = "$last_node\0$node"; $links{$link} = 1; } $last_node = $node; } } if ($num_links > 0) { foreach my $id (0..$#nodes_ordered) { $node2id{$nodes_ordered[$id]} = $id; } } if ($num_paths > 0) { foreach my $id (0..$#links_ordered) { $link2id{$links_ordered[$id]} = $id; } } return ($num_nodes, $num_links, $num_paths, $num_pathLinks); } sub GetNumPathLinks { my %links; foreach my $path (keys %path2value) { my $last_node; foreach my $node ( split /\0/, $path ) { } } my $num_pathLinks = keys %links; return $num_pathLinks; } ############################################################################ sub print_links { print $filehandle " \@links=" if defined ($verbose); print $filehandle "[\n"; my $first_time = 1; foreach my $id (0..$#links_ordered) { my $link = $links_ordered[$id]; my ($src, $dst) = split /\0/, $link; my $src_id = $node2id{$src}; my $dst_id = $node2id{$dst}; unless ($first_time == 1) { print($filehandle ",\n"); } print $filehandle " " if (defined $verbose); print $filehandle "{"; print $filehandle " \@source=" if (defined $verbose); print $filehandle "$src_id;"; print $filehandle " \@destination=" if (defined $verbose); print $filehandle "$dst_id;}"; $first_time = 0; } print $filehandle "\n"; print $filehandle " " if (defined $verbose); print $filehandle "];\n"; } sub print_paths { print $filehandle " \@paths=" if (defined $verbose); print $filehandle "[\n"; my $first_time = 1; foreach my $path_id (0..$#paths_ordered) { my $last_node; my @ids; foreach my $node (split /\0/, $paths_ordered[$path_id]) { if (defined $last_node) { my $link = "$last_node\0$node"; push @ids, $link2id{$link}; } $last_node = $node; } unless ($first_time == 1) { print($filehandle ",\n"); } print $filehandle " " if (defined $verbose); print $filehandle "{ [ ", join(", ", @ids), "]; }"; $first_time = 0; } print $filehandle "\n"; print $filehandle " " if (defined $verbose); print $filehandle "];\n"; } ############################################################################ sub print_attribute { my ($name, $type, $default) = @_; my $printer; if ($type eq "string") { $printer = sub { my ($s) = @_; $s =~ s/\\/\\\\/g; $s =~ s/\"/\\\"/g; return "\"$s\""; }; } elsif ($type eq "int" || $type eq "double") { $printer = sub { my ($n) = @_; return sprintf("%d",$n); }; } elsif ($type eq "float") { $printer = sub { my ($n) = @_; return $n."f"; }; } elsif ($type eq "bool") { $printer = sub { my ($n) = @_; $n =~ y/A-Z/a-z/; if ($n eq "true" || $n eq "t") { return "T"; } else { return "F"; } }; } else { die("Unknown type: `$type'"); } print $filehandle " " if (defined $verbose); print $filehandle "{\n"; print $filehandle " \@name=" if (defined $verbose); print $filehandle "\$$name;\n"; print $filehandle " \@type=" if (defined $verbose); print $filehandle "$type;\n"; print $filehandle " \@default=" if (defined $verbose); if (defined $default) { print $filehandle $default; } print $filehandle ";\n"; print $filehandle " \@nodeValues=" if (defined $verbose); print $filehandle "["; print_attributes_helper($name, \%node2value, \@nodes_ordered, $printer); print $filehandle " " if (defined $verbose); print $filehandle "];\n"; print $filehandle " \@linkValues=" if (defined $verbose); print $filehandle "["; print_attributes_helper($name, \%link2value, \@links_ordered, $printer); print $filehandle " " if (defined $verbose); print $filehandle "];\n"; print $filehandle " \@pathValues=" if (defined $verbose); print $filehandle "["; print_attributes_helper($name, \%link2value, \@paths_ordered, $printer); print $filehandle " " if (defined $verbose); print $filehandle "];\n"; print $filehandle " " if (defined $verbose); print $filehandle "}"; } sub print_attributes_helper { my ($name, $objects, $objects_ordered, $printer) = @_; my $first_time = 1; my $num_effected_objects = 0; foreach my $id (0..$#$objects_ordered) { my $value = $objects->{$objects_ordered->[$id]}->{$name}; if (defined $value) { unless (1 == $first_time) { print $filehandle ",\n"; } else { print $filehandle "\n"; } $first_time = 0; my $format; if (defined $verbose) { $format = " { \@id=%d; \@value=%s; }"; } else { $format = "{%d;%s;}"; } printf $filehandle $format, $id, &$printer($value); $num_effected_objects++; } } print $filehandle "\n" if ($num_effected_objects > 0); } ############################################################################ sub print_attribute_to_color { my ($color_type, $name, $type) = @_; my $printer; if ($color_type eq "hot_to_cold") { if ($type eq "string") { $printer = print_string_attribute_to_color_hot_to_cold_printer( $name, $type); } elsif ($type eq "int" || $type eq "double" || $type eq "float") { $printer = print_int_attribute_to_color_hot_to_cold_printer( $name, $type); } elsif ($type eq "bool") { $printer = sub { my ($n) = @_; if ($n eq "T" || $n eq "t") { return (200<<16) | (200<<8) | (200); } else { return (100<<16) | (100<<8) | (100); } }; } else { die("Unknown type: `$type'"); } } else { die("Unknown color type: `$color_type' used for attribute " ."`$name'"); } print $filehandle " " if (defined $verbose); print $filehandle "{\n"; print $filehandle " \@name=" if (defined $verbose); print $filehandle "\$color_by_$name;\n"; print $filehandle " \@type=" if (defined $verbose); print $filehandle "int;\n"; print $filehandle " \@default=" if (defined $verbose); print $filehandle ";\n"; print $filehandle " \@nodeValues=" if (defined $verbose); print $filehandle "["; print_attributes_helper($name, \%node2value, \@nodes_ordered, $printer); print $filehandle " " if (defined $verbose); print $filehandle "];\n"; print $filehandle " \@linkValues=" if (defined $verbose); print $filehandle "["; print_attributes_helper($name, \%link2value, \@links_ordered, $printer); print $filehandle " " if (defined $verbose); print $filehandle "];\n"; print $filehandle " \@pathValues=" if (defined $verbose); print $filehandle "["; print_attributes_helper($name, \%path2value, \@paths_ordered, $printer); print $filehandle " " if (defined $verbose); print $filehandle "];\n"; print $filehandle " " if (defined $verbose); print $filehandle "}"; } sub print_string_attribute_to_color_hot_to_cold_printer { my ($name, $type) = @_; my %string2frequency; foreach my $node (@nodes_ordered) { if (defined $node2value{$node}{$name} ) { $string2frequency{$node2value{$node}{$name}}++; } } foreach my $link (@links_ordered) { if (defined $link2value{$link}{$name} ) { $string2frequency{$link2value{$link}{$name}}++; } } my @strings = sort {$string2frequency{$b} <=> $string2frequency{$a}} keys %string2frequency; my %string2color; my $i = 0; #while ($#strings > -1 && $i < $MAX_NUM_STR_COLORS) { while ($#strings > -1) { $string2color{shift @strings} = compute_integer_hot_to_cold($i++/($MAX_NUM_STR_COLORS+2)); } my $printer = sub { my ($n) = @_; my $string2color = \%string2color; if (defined $string2color->{$n}) { return $string2color->{$n}; } else { return compute_integer_hot_to_cold( ($MAX_NUM_STR_COLORS+1)/($MAX_NUM_STR_COLORS+2)); } }; return $printer; } sub print_int_attribute_to_color_hot_to_cold_printer { my ($name, $type) = @_; my ($min, $max); foreach my $object2value (\%node2value, \%link2value, \%path2value) { foreach my $object (keys %{$object2value}) { my $v = $object2value->{$object}->{$name}; if (defined $v) { if (!defined $min) { $min = $max = $v; } elsif ($v < $min) { $min = $v; } elsif ($v > $max) { $max = $v; } } } } my $printer = sub { my ($n) = @_; my $value; if ($max-$min == 0) { $value = 1; } else { $value = ($n-$min)/($max-$min); } return compute_integer_hot_to_cold($value); }; return $printer; } ############################################################################ sub print_qualifier { my ($type, $name, $attributes) = @_; unless (defined $type) { die("Graph:: Qualifier with out type"); } unless (defined $name) { die("Graph:: Qualifier with out name"); } print $filehandle " " if (defined $verbose); print $filehandle "{\n"; print $filehandle " \@type=" if (defined $verbose); print $filehandle "\$$type;\n"; print $filehandle " \@name=" if (defined $verbose); print $filehandle "\$$name;\n"; print $filehandle " \@description=" if (defined $verbose); print $filehandle ";\n"; print $filehandle " \@attributes=" if (defined $verbose); print $filehandle "[\n"; foreach my $i (0..$#$attributes) { my $attribute = $attributes->[$i]; my $id = $attribute2id{$attribute}; unless (defined $id) { die("Graph::qualifier type:$type name:$name requires " ." $attribute\n"); } my $format; if (defined $verbose) { $format = " { \@attribute=%s; \@alias=\$%s; }"; } else { $format = "{%s;\$%s;}"; } printf $filehandle $format, $id, $attribute; if ($i < $#$attributes) { print $filehandle ",\n"; } else { print $filehandle "\n"; } } print $filehandle " " if (defined $verbose); print $filehandle "];\n"; print $filehandle " " if (defined $verbose); print $filehandle "}"; } ############################################################################ # Code adapted from # , # "Colour Ramping for Data Visualization", by Paul Bourke, July 1996. sub compute_integer_hot_to_cold { my ($x) = @_; my $r = 255; my $g = 255; my $b = 255; if ($x < 0.0) { $x = 0.0; } elsif ($x > 1.0) { $x = 1.0; } if ($x < 0.25) { $r = 0; $g = int(255.0 * 4.0 * $x); } elsif ($x < 0.5) { $r = 0; $b = int(255.0 + 255.0 * 4.0 * (0.25 - $x)); } elsif ($x < 0.75) { $r = int(255.0 * 4.0 * ($x - 0.5)); $b = 0; } else { $g = int(255.0 + 255.0 * 4.0 * (0.75 - $x)); $b = 0; } return ($r << 16) | ($g << 8) | $b; } 1;