XML-API

 view release on metacpan or  search on metacpan

lib/XML/API.pm  view on Meta::CPAN

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self  = {
        attrs    => {},
        contents => [],
        @_
    };

    if ( $self->{comment} ) {
        $self->{comment} =~ s/--/- -/go;
    }

    weaken( $self->{parent} ) if ( exists $self->{parent} );

    bless( $self, $class );
    return $self;
}

sub parent {
    my $self = shift;
    return $self->{parent};
}

sub inline {
    my $self = shift;
    return $self->{inline};
}

sub attrs_as_string {
    my $self = shift;
    my @strings;

    foreach my $key ( sort keys %{ $self->{attrs} } ) {
        my $val = $self->{attrs}->{$key};
        if ( !defined($val) ) {
            warn "Attribute '$key' (element '$self->{element}') is undefined";
            $val = '*undef*';
        }
        push( @strings, $key . '="' . $val . '"' );
    }

    return '' unless (@strings);
    return ' ' . join( ' ', @strings );
}

sub add {
    my $self = shift;
    push( @{ $self->{contents} }, @_ );
}

sub as_string {
    my $self       = shift;
    my $indent     = shift || '';
    my $growindent = shift || '';

    if ( $self->{comment} ) {
        return $indent . '<!-- ' . $self->{comment} . ' -->';
    }

    if ( $self->{cdata} ) {
        return $indent . '<![CDATA[' . $self->{cdata} . ']]>';
    }

    if ( !@{ $self->{contents} } ) {
        return
            $indent . '<'
          . ( $self->{ns} ? $self->{ns} . ':' : '' )
          . $self->{element}
          . $self->attrs_as_string . ' />';
    }

    my $str =
        $indent . '<'
      . ( $self->{ns} ? $self->{ns} . ':' : '' )
      . $self->{element}
      . $self->attrs_as_string . '>';
    my $complex = 0;

    foreach my $c ( @{ $self->{contents} } ) {
        if ( eval { $c->isa(__PACKAGE__) and !$c->inline } ) {
            $complex = 1;
            $str .= "\n" . $c->as_string( $indent . $growindent, $growindent );
        }
        elsif ( eval { $c->isa('XML::API') } ) {    # assume it is complex?
            $str .= "\n"
              . join( "\n",
                map { $_->as_string( $indent . $growindent, $growindent ) }
                  $c->_elements );
        }
        else {
            $str .= $c if ( defined($c) );
        }
    }

    if ($complex) {
        $str .= "\n" . $indent;
    }
    $str .=
      '</' . ( $self->{ns} ? $self->{ns} . ':' : '' ) . $self->{element} . '>';
    return $str;
}

sub fast_string {
    my $self = shift;

    $self->{comment} && return '';
    $self->{cdata}   && return '<![CDATA[' . $self->{cdata} . ']]>';

    return
        '<'
      . ( $self->{ns} ? $self->{ns} . ':' : '' )
      . $self->{element}
      . $self->attrs_as_string . ' />'
      unless @{ $self->{contents} };

    return
        '<'
      . ( $self->{ns} ? $self->{ns} . ':' : '' )
      . $self->{element}
      . $self->attrs_as_string . '>'
      . join(
        '',
        map {
            eval { $_->isa(__PACKAGE__) } ? $_->fast_string
              : (
                eval { $_->isa('XML::API') }
                ? join( '', map { $_->fast_string } $_->_elements )
                : $_
              )
        } @{ $self->{contents} }
      )
      . '</'
      . ( $self->{ns} ? $self->{ns} . ':' : '' )
      . $self->{element} . '>';
}

# Private package (not to be used outside XML::API)
package XML::API::SAXHandler;
use strict;
use warnings;
use base qw(XML::SAX::Base);

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self  = {
        xmlapi => undef,
        @_,
    };
    bless( $self, $class );
    return $self;
}

sub start_element {
    my $self = shift;
    my $hash = shift;
    if ( $hash->{Name} eq '_xml_api_ignore' ) {
        $self->{xml_api_ignore} = 1;
        return;
    }
    $self->{xml_api_ignore} = 0;

    my $attrs = {};
    foreach my $val ( values %{ $hash->{Attributes} } ) {
        $attrs->{ $val->{Name} } = $val->{Value};
    }

lib/XML/API.pm  view on Meta::CPAN

            $self->_element( $e, $val );
            next;
        }

        my $attr     = {};
        my @contents = ();

        if ( ref($val) and ref($val) eq 'ARRAY' ) {
            my @val = @$val;

            foreach my $i ( 1 .. int( scalar(@val) / 2 ) ) {
                my ( $arg, $arg2 ) = splice( @val, 0, 2 );

                if ( $arg =~ s/^-(.+)/$1/o ) {
                    $attr->{$arg} = $arg2;
                }
                elsif ( ref($arg2) and ref($arg2) eq 'ARRAY' ) {
                    push( @contents, [ $arg, $arg2 ] );
                }
                else {
                    push( @contents, { $arg => $arg2 } );
                }
            }

            push( @contents, @val ) if (@val);
        }
        else {
            push( @contents, $val );
        }
        $self->_open( $e, $attr );

        foreach my $c (@contents) {
            if ( ref($c) and ref($c) eq 'ARRAY' ) {
                $self->_ast(@$c);
            }
            elsif ( ref($c) and ref($c) eq 'HASH' ) {
                my ( $k, $v ) = each %$c;
                $self->_open($k);
                $self->_add($v);
                $self->_close($k);
            }
            else {
                $self->_add($c);
            }
        }

        $self->_close($e);
    }

    return;
}

sub _comment {
    my $self = shift;

    # FIXME: should escape?
    $self->_raw( XML::API::Element->new( comment => join( '', @_ ) ) );
    return;
}

sub _cdata {
    my $self = shift;
    $self->_raw( XML::API::Element->new( cdata => join( '', @_ ) ) );
    return;
}

sub _css {
    my $self    = shift;
    my $content = shift;
    if ( $content =~ /\n/s ) {
        $self->_raw( '/*<![CDATA[*/' . "\n" . $content . '/*]]>*/' );
    }
    else {
        $self->_raw( '/*<![CDATA[*/ ' . $content . ' /*]]>*/' );
    }
    return;
}

sub _javascript {
    my $self = shift;
    $self->script_open( -type => 'text/javascript' );
    $self->_raw( '// -------- JavaScript Begin -------- <![CDATA[' . "\n" );
    $self->_raw(@_);
    $self->_raw('// --------- JavaScript End --------- ]]>');
    $self->script_close;
    return;
}

sub _parse {
    my $self    = shift;
    my $current = $self->{current};

    foreach (@_) {
        next unless ( defined($_) and $_ ne '' );
        local $XML::SAX::ParserPackage = 'XML::LibXML::SAX';
        my $parser =
          XML::SAX::ParserFactory->parser(
            Handler => XML::API::SAXHandler->new( xmlapi => $self ), );

        # remove leading and trailing space, otherwise SAX barfs at us.
        ( my $t = $_ ) =~ s/(^\s+)|(\s+$)//go;

        # escape '&' as well
        $t =~ s/\&(\w+\;)/__AMP__$1/go;

        # escape '&' in urls
        $t =~ s/\&(\w+=)/__AMP__amp;$1/go;
        $parser->parse_string(
            '<_xml_api_ignore>' . $t . '</_xml_api_ignore>' );
    }

    # always make sure that we finish where we started
    $self->{current} = $current;
}

sub _parse_chunk {
    my $self    = shift;
    my $current = $self->{current};

    foreach (@_) {
        next unless ( defined($_) and $_ ne '' );
        local $XML::SAX::ParserPackage = 'XML::LibXML::SAX';
        my $parser =

lib/XML/API.pm  view on Meta::CPAN

element the same. Returns a reference (private data type) to the new
element which can be used in the _goto function below.

This is effectively the same as the following:

    $x->element_open(-attribute => $value, -attr2=>'val2');
    $x->_add($content);
    $x->element_close;

If $content is not given (or never added with the _add method) for an
element then it will be rendered as empty. Ie, $x->br() produces:

    <br />

=head2 $x->_element('element',...)

The generic implementation of $x->element. Useful if your element names
are not suitable as Perl method calls, or are otherwise funny (eg
starting with '_').

=head2 $x->element_raw('raw content',...)

Adds unescaped content inside an element named 'element'. This is a
shortcut for the case where you find yourself doing the following:

    $x->element_open();
    $x->_raw($content);
    $x->element_close();

=head2 $x->ns__element_open(...)

Same as $x->element_open but prefixed with an XML namespace. Equivalent
to the following.

  $x->_ns('ns');
  $x->element_open(...);
  ...
  $x->element_close;
  $x->_ns(undef);

=head2 $x->ns__element(...)

Same as $x->element but prefixed with an XML namespace. Equivalent to
the following.

  $x->_ns('ns');
  $x->element(...);
  $x->_ns(undef);

=head2 $x->_comment($comment)

Add an XML comment to $x. Is almost the same as this:

    $x->_raw("\n<!--");
    $x->_raw($content);
    $x->_raw('-->')

Except that indentation is correct. Any occurences of '--' in $content
will be replaced with '- -'.

=head2 $x->_cdata($content)

A shortcut for $x->_raw("\n<![CDATA[", $content, " ]]>");

=head2 $x->_css($content )

Adds $content inside a pair of CDATA tags which are encapsulated inside
CSS comments. Similar to:

 $x->_raw('/*<![CDATA[*/ '. $content .' /*]]>*/');

=head2 $x->_javascript($script )

A shortcut for adding $script inside a pair of <script
type="text/javascript"> elements and a _CDATA tag.

=head2 $x->_parse(@content)

Adds content to the current element, but will parse it for xml elements
and add them as method calls. Regardless of $content (missing end tags
etc) the current element will remain the same. Relies on XML::SAX to do
the parsing using the "parse_string" method. In this case XML::SAX
requires that the content is a complete xml document.

=head2 $x->_parse_chunk(@content)

Adds content to the current element, but will parse it for xml elements
and add them as method calls. Regardless of $content (missing end tags
etc) the current element will remain the same. Relies on XML::SAX to do
the parsing, but using the "parse_chunk" method. This method is
suitable for parsing xml fragments which are not necessarily complete.

=head2 $x->_ast(@content)

Sometimes you may want to just build some kind of abstract syntax tree
structure and just feed it to XML::API without having to make all the
method calls yourself. This method lets you do just that.

The following input:

  p => [
      label => 'Body',
      textarea => [
          -rows  => 10,
          -cols  => 50,
          -name  => 'body',
          'the body',
      ],
  ],

results in the following xml:

  <p>
    <label>Body</label>
    <textarea cols="50" name="body" rows="10">the body</textarea>
  </p>


=head2 $x->_attrs( )

Allows you to get/set the attributes of the current element. Accepts



( run in 0.859 second using v1.01-cache-2.11-cpan-995e09ba956 )