XML-XSPF

 view release on metacpan or  search on metacpan

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

	}

	# Playlists MUST have a <trackList> element (even if it's empty version 1)
	if (!$parser->{'_xspf'}->{'trackListCount'}) {

		Carp::confess("Error while parsing playlist - no trackList element!\n");
		return undef;
	}

	$parser = undef;

	return $self;
}

# Create a XSPF document from our in-memory version.
sub toString {
	my $self   = shift;

	my $string = undef;

	my $writer = XML::Writer->new(
		'OUTPUT'      => \$string,
		'DATA_MODE'   => 1,
		'DATA_INDENT' => 4,
	);

	$writer->xmlDecl("UTF-8");

	$writer->startTag('playlist', 'version' => $self->version, 'xmlns' => $self->xmlns);

	for my $element (qw(title creator annotation info location identifier image date license)) {

		if (my $value = $self->$element) {

			$writer->dataElement($element, $value);
		}
	}

	if ($self->attributions) {

		$writer->startTag('attribution');

		for my $attribution ($self->attributions) {

			$writer->dataElement(@{$attribution});
		}

		$writer->endTag('attribution');
	}

	if ($self->trackList) {

		$writer->startTag('trackList');

		for my $track ($self->trackList) {

			$writer->startTag('track');

			for my $element (qw(location identifier)) {

				for my $cdata (@{$track->get("${element}s")}) {

					$writer->dataElement($element, $cdata);
				}
			}

			for my $element (qw(link meta)) {

				for my $cdata (@{$track->get("${element}s")}) {

					$writer->startTag($element, 'rel' => $cdata->[0]);
					$writer->characters($cdata->[1]);
					$writer->endTag($element);
				}
			}

			for my $element (qw(title creator annotation info image album trackNum duration)) {

				if (my $value = $track->$element) {

					$writer->dataElement($element, $value);
				}
			}

			$writer->endTag('track');
		}

		$writer->endTag('trackList');
	}

	$writer->endTag('playlist');
	$writer->end;

	# Don't escape these. XML::Writer provides some basic escaping, but not all.
	# http://rt.cpan.org/Ticket/Display.html?id=36778
	# $string = encode_entities($string, '^\n\r\t !\#\$%\(-;=?-~<>&"');

	return $string;
}

sub handleStartElement {
	my ($parser, $element, %attributes) = @_;

	# Poor Man's HTML checker - XML::Parser treats elements.
	# So look at the previous element, and if we're a single value
	# element, fail.
	if ($parser->{'_xspf'}->{'path'}) {

		my @parts = split(/\//, $parser->{'_xspf'}->{'path'});
		my $last  = pop @parts;

		if (grep { /^$last$/ } @singleValueElements) {

			Carp::confess("Found HTML markup in <$last>\n");
		}
	}

	my $path = $parser->{'_xspf'}->{'path'} .= "/$element";
	my $self = $parser->{'_xspf'}->{'self'};

	push @{ $parser->{'_xspf'}->{'states'} }, {
		'attributes' => \%attributes,
		'cdata'      => '',
		'path'       => $path,
	};

	# Set some default types once we encounter them.
	if ($path eq '/playlist/attribution') {

		if ($parser->{'_xspf'}->{'attributionCount'}) {

			Carp::confess("Too many attribution elements in playlist!\n");
		}

		$self->set('attributions', []);

		$parser->{'_xspf'}->{'attributionCount'} = 1;
	}

	if ($path eq '/playlist/meta' ||
	    $path eq '/playlist/link') {

		$self->set("${element}s", []);
	}

	if ($path eq '/playlist/trackList') {

		if ($parser->{'_xspf'}->{'trackListCount'}) {

			Carp::confess("Too many trackList elements in playlist!\n");
		}

		$parser->{'_xspf'}->{'trackListCount'} = 1;
	}

	# We got a track entry - create a new object for it
	if ($path eq '/playlist/trackList/track') {

		$parser->{'_xspf'}->{'track'} = XML::XSPF::Track->new;
	}
}

sub handleCharElement {
	my ($parser, $value) = @_;

	# Keep the our little state machine chugging along
	my $state = pop @{ $parser->{'_xspf'}->{'states'} };

	$state->{'cdata'} .= $value;

	push @{ $parser->{'_xspf'}->{'states'} }, $state;
}

sub handleEndElement {
	my ($parser, $element) = @_;

	my $state = pop @{ $parser->{'_xspf'}->{'states'} };
	my $value = $state->{'cdata'};

	my $path  = $parser->{'_xspf'}->{'path'};
	my $self  = $parser->{'_xspf'}->{'self'};

	# These are all single value elements.
	if ($path eq '/playlist/annotation' || 
	    $path eq '/playlist/creator'    || 
	    $path eq '/playlist/date'       || 
	    $path eq '/playlist/identifier' || 
	    $path eq '/playlist/image'      || 
	    $path eq '/playlist/info'       || 
	    $path eq '/playlist/license'    ||
	    $path eq '/playlist/location'   || 
	    $path eq '/playlist/title') {

		# There should only be one value per track according to the spec.
		if ($self->get($element)) {

			Carp::confess("Element: $path has too many values!\n");
		}

		if (_validateLinkElement($path, 'playlist', $element, $value)) {

			$self->$element($value);
		}
	}

	if ($path eq '/playlist/attribution/identifier' ||
	    $path eq '/playlist/attribution/location') {

		if (_validateLinkElement($path, 'playlist', $element, $value)) {

			$self->append('attributions', [ $element, $value ]);
		}
	}

	if ($path eq '/playlist/meta' ||
	    $path eq '/playlist/link') {

		my $rel = $state->{'attributes'}->{'rel'};

		# Check both the value and the rel for validity.
		if (_validateLinkElement($path, 'playlist', $element, $value, $rel)) {

			$self->append("${element}s", [ $rel, $value ]);
		}
	}

	# We've hit the end of a track definition - push it onto the end of the track list.
	if ($path eq '/playlist/trackList/track') {

		push @{ $parser->{'_xspf'}->{'tracks'} }, $parser->{'_xspf'}->{'track'};
	}

	# End of the trackList - set all the tracks we've acquired.
	if ($path eq '/playlist/trackList') {

		$self->trackList($parser->{'_xspf'}->{'tracks'});
	}



( run in 0.834 second using v1.01-cache-2.11-cpan-39bf76dae61 )