| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #======================================================================== | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | # Badger::Codecs | 
| 4 |  |  |  |  |  |  | # | 
| 5 |  |  |  |  |  |  | # DESCRIPTION | 
| 6 |  |  |  |  |  |  | #   Manager of Badger::Codec modules for encoding and decoding data. | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | # AUTHOR | 
| 9 |  |  |  |  |  |  | #   Andy Wardley | 
| 10 |  |  |  |  |  |  | # | 
| 11 |  |  |  |  |  |  | #======================================================================== | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | package Badger::Codecs; | 
| 14 |  |  |  |  |  |  |  | 
| 15 | 15 |  |  | 15 |  | 6487 | use Carp; | 
|  | 15 |  |  |  |  | 28 |  | 
|  | 15 |  |  |  |  | 923 |  | 
| 16 | 15 |  |  | 15 |  | 5046 | use Badger::Codec::Chain 'CHAIN CHAINED'; | 
|  | 15 |  |  |  |  | 37 |  | 
|  | 15 |  |  |  |  | 120 |  | 
| 17 |  |  |  |  |  |  | use Badger::Factory::Class | 
| 18 | 15 |  |  |  |  | 134 | version   => 0.01, | 
| 19 |  |  |  |  |  |  | debug     => 0, | 
| 20 |  |  |  |  |  |  | item      => 'codec', | 
| 21 |  |  |  |  |  |  | path      => 'Badger(X)::Codec', | 
| 22 |  |  |  |  |  |  | import    => 'class CLASS', | 
| 23 |  |  |  |  |  |  | constants => 'HASH ARRAY DELIMITER PKG', | 
| 24 |  |  |  |  |  |  | constant  => { | 
| 25 |  |  |  |  |  |  | CODEC_METHOD  => 'codec', | 
| 26 |  |  |  |  |  |  | ENCODE_METHOD => 'encode', | 
| 27 |  |  |  |  |  |  | DECODE_METHOD => 'decode', | 
| 28 |  |  |  |  |  |  | ENCODING      => 'Badger::Codec::Encoding', | 
| 29 |  |  |  |  |  |  | }, | 
| 30 |  |  |  |  |  |  | exports   => { | 
| 31 |  |  |  |  |  |  | any   => 'Codec', | 
| 32 | 15 |  |  | 15 |  | 5943 | }; | 
|  | 15 |  |  |  |  | 36 |  | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | our $CODECS     = { | 
| 35 |  |  |  |  |  |  | # any codecs with non-standard capitalisation can go here, but | 
| 36 |  |  |  |  |  |  | # generally we grok the module name from the $CODEC_PATH, e.g. | 
| 37 |  |  |  |  |  |  | uri      => 'Badger::Codec::URI', | 
| 38 |  |  |  |  |  |  | url      => 'Badger::Codec::URL', | 
| 39 |  |  |  |  |  |  | yaml     => 'Badger::Codec::YAML', | 
| 40 |  |  |  |  |  |  | json     => 'Badger::Codec::JSON', | 
| 41 |  |  |  |  |  |  | html     => 'Badger::Codec::HTML', | 
| 42 |  |  |  |  |  |  | tt       => 'Badger::Codec::TT', | 
| 43 |  |  |  |  |  |  | map { | 
| 44 |  |  |  |  |  |  | my $name = $_; $name =~ s/\W//g; | 
| 45 |  |  |  |  |  |  | $_ => [ENCODING, ENCODING.PKG.$name], | 
| 46 |  |  |  |  |  |  | } qw( utf8 UTF8 UTF16BE UTF16LE UTF32BE UTF32LE ) | 
| 47 |  |  |  |  |  |  | }; | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | sub Codec { | 
| 51 | 0 |  |  | 0 | 1 | 0 | CLASS->codec(@_); | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | sub codec { | 
| 56 | 54 |  |  | 54 | 1 | 209 | my $self = shift->prototype; | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | # quick turn-around if we're handling chains | 
| 59 | 54 | 100 |  |  |  | 281 | return $_[0] =~ CHAINED | 
| 60 |  |  |  |  |  |  | ? $self->chain(@_) | 
| 61 |  |  |  |  |  |  | : $self->item(@_); | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | sub chain { | 
| 66 | 4 |  |  | 4 | 1 | 7 | my $self = shift; | 
| 67 | 4 | 50 |  |  |  | 7 | $self->debug("creating chain for $_[0]\n") if $DEBUG; | 
| 68 | 4 |  |  |  |  | 16 | return CHAIN->new(@_); | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | sub found_object { | 
| 73 | 25 |  |  | 25 | 1 | 43 | my ($self, $name, $item, $args) = @_; | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | # TODO: assert $item is a codec object? | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | # We cache any codecs that are created without configuration items | 
| 78 |  |  |  |  |  |  | # but we can only use those "bare" codecs if $args is empty.  Otherwise | 
| 79 |  |  |  |  |  |  | # we must create a new object | 
| 80 | 25 | 100 |  |  |  | 42 | if (@$args) { | 
| 81 | 1 |  |  |  |  | 2 | $self->debug("creating new ", ref $item, " codec for $name\n") if DEBUG; | 
| 82 | 1 |  |  |  |  | 4 | return $self->construct($name, ref $item, $args); | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  | else { | 
| 85 | 24 | 50 |  |  |  | 39 | $self->debug("re-using cached codec for $name: $item\n") if $DEBUG; | 
| 86 | 24 |  |  |  |  | 46 | return $item; | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | sub result { | 
| 92 | 50 |  |  | 50 | 1 | 87 | my ($self, $name, $codec, $args) = @_; | 
| 93 |  |  |  |  |  |  | # only cache codec objects created with no arguments | 
| 94 | 50 | 100 |  |  |  | 99 | unless (@$args) { | 
| 95 | 49 |  |  |  |  | 53 | $self->debug("Caching $name codec for subsequent re-used: $codec") if DEBUG; | 
| 96 | 49 |  |  |  |  | 77 | $self->{ codecs }->{ $name } = $codec; | 
| 97 |  |  |  |  |  |  | } | 
| 98 | 50 |  |  |  |  | 174 | return $codec; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | sub encode { | 
| 103 | 8 |  |  | 8 | 1 | 61 | shift->codec(shift)->encode(@_); | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | sub decode { | 
| 108 | 8 |  |  | 8 | 1 | 34 | shift->codec(shift)->decode(@_); | 
| 109 |  |  |  |  |  |  | } | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 113 |  |  |  |  |  |  | # export hooks | 
| 114 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | class->exports( | 
| 117 |  |  |  |  |  |  | hooks => { | 
| 118 |  |  |  |  |  |  | map { ($_ => \&_export_hook) } | 
| 119 |  |  |  |  |  |  | qw( codec codecs ) | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | ); | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | sub _export_hook { | 
| 125 | 15 |  |  | 15 |  | 50 | my ($class, $target, $key, $symbols) = @_; | 
| 126 | 15 | 50 |  |  |  | 44 | croak "You didn't specify a value for the '$key' load option." | 
| 127 |  |  |  |  |  |  | unless @$symbols; | 
| 128 | 15 |  |  |  |  | 30 | my $method = "export_$key"; | 
| 129 | 15 |  |  |  |  | 44 | $class->$method($target, shift @$symbols); | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | sub export_codec { | 
| 134 | 19 |  |  | 19 | 1 | 43 | my ($class, $target, $name, $alias) = @_; | 
| 135 | 19 |  |  |  |  | 52 | my $codec   = $class->codec($name); | 
| 136 | 19 |  | 100 |  |  | 91 | my $cmethod = $alias || CODEC_METHOD; | 
| 137 | 19 | 100 |  |  |  | 59 | my $emethod = $alias ? join('_', ENCODE_METHOD, $alias) : ENCODE_METHOD; | 
| 138 | 19 | 100 |  |  |  | 44 | my $dmethod = $alias ? join('_', DECODE_METHOD, $alias) : DECODE_METHOD; | 
| 139 | 15 |  |  | 15 |  | 134 | no strict 'refs'; | 
|  | 15 |  |  |  |  | 30 |  | 
|  | 15 |  |  |  |  | 3794 |  | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | # prefix target class onto above method names | 
| 142 | 19 |  |  |  |  | 80 | $_= "${target}::$_" for ($cmethod, $emethod, $dmethod); | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 19 | 50 |  |  |  | 71 | $class->debug("exporting $codec codec to $target\n") if $DEBUG; | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | # NOTE: I think it's more correct to attempt the export regardless of | 
| 147 |  |  |  |  |  |  | # any existing sub and allow a redefine warning to be raised.  This is | 
| 148 |  |  |  |  |  |  | # better than silently failing to export the requested items. | 
| 149 | 19 |  |  |  |  | 25 | my $temp = $codec; # make sure this is a constant on 5.22 | 
| 150 | 19 |  |  | 0 |  | 124 | *{$cmethod} = sub() { $temp };  # unless defined &{$cmethod}; | 
|  | 19 |  |  |  |  | 85 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 151 | 19 |  |  |  |  | 83 | *{$emethod} = $codec->encoder;  # unless defined &{$emethod}; | 
|  | 19 |  |  |  |  | 66 |  | 
| 152 | 19 |  |  |  |  | 70 | *{$dmethod} = $codec->decoder;  # unless defined &{$dmethod}; | 
|  | 19 |  |  |  |  | 94 |  | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | sub export_codecs { | 
| 157 | 3 |  |  | 3 | 1 | 7 | my ($class, $target, $names) = @_; | 
| 158 | 3 | 100 |  |  |  | 5 | if (ref $names eq HASH) { | 
| 159 | 1 |  |  |  |  | 14 | while (my ($key, $value) = each %$names) { | 
| 160 | 1 |  |  |  |  | 5 | $class->export_codec($target, $value, $key); | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  | else { | 
| 164 | 2 | 50 |  |  |  | 14 | $names = [ split(DELIMITER, $names) ] unless ref $names eq ARRAY; | 
| 165 | 2 |  |  |  |  | 9 | $class->export_codec($target, $_, $_) for @$names; | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | 1; | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | __END__ |