From: <hoe...@us...> - 2004-09-19 21:02:19
|
Update of /cvsroot/perl-css/CSS-SAC/lib/CSS/SAC/Selector In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4647 Added Files: ToXPath.pm Log Message: Experimental Selector 2 XPath converter --- NEW FILE: ToXPath.pm --- # ToXPath.pm -- Convert CSS Selectors to XPath # # $Id: ToXPath.pm,v 1.1 2004/09/19 21:02:11 hoehrmann Exp $ # todo: haven't looked at the code for years, # probably needs some work... sub CSS::SAC::Selector::Sibling::_GetElement { shift->SiblingSelector->_GetElement } sub CSS::SAC::Selector::Descendant::_GetElement { shift->SimpleSelector->_GetElement } sub CSS::SAC::Selector::Conditional::_GetElement { shift->SimpleSelector->_GetElement } sub CSS::SAC::Selector::Element::_GetElement { shift } sub CSS::SAC::Condition::Content::ToXPath { sprintf "contains(., '%s')", shift->Data } sub CSS::SAC::Condition::Lang::ToXPath { sprintf "lang('%s')", shift->Lang } sub CSS::SAC::Condition::Attribute::ToXPath { my $cond = shift; my $name = $cond->LocalName; my $nsur = $cond->NamespaceURI; my $valu = $cond->Value; my $apfx = ""; $apfx .= "local-name() = '$name'" if defined $name; $apfx .= " and " if defined $nsur and length $apfx; $apfx .= "namespace-uri() = '$nsur'" if defined $nsur; $apfx = "[$apfx]" if length $apfx; if ($cond->is_type(ID_CONDITION)) { sprintf "id('%s') and count(id('%s')/preceding::* | ". "id('%s')/ancestor::*) = count(preceding::* | ancestor::*)", $valu, $valu, $valu } elsif ($cond->is_type(ATTRIBUTE_CONDITION) && $cond->Specified) { sprintf "\@*%s[. = '%s']", $apfx, $valu } elsif ($cond->is_type(ATTRIBUTE_CONDITION)) { sprintf "\@*%s", $apfx } elsif ($cond->is_type(STARTS_WITH_ATTRIBUTE_CONDITION)) { sprintf "\@*%s[starts-with(., '%s')]", $apfx, $valu } elsif ($cond->is_type(CONTAINS_ATTRIBUTE_CONDITION)) { sprintf "\@*%s[contains(., '%s')]", $apfx, $valu } elsif ($cond->is_type(BEGIN_HYPHEN_ATTRIBUTE_CONDITION)) { sprintf "\@*%s[. = '%s' or starts-with(., '%s-')]", $apfx, $valu, $valu } elsif ($cond->is_type(ENDS_WITH_ATTRIBUTE_CONDITION)) { sprintf "\@*%s[substring(., string-length(.) - ". "string-length('%s') + 1) = '%s']", $apfx } elsif ($cond->is_type(ONE_OF_ATTRIBUTE_CONDITION)) { sprintf "\@*%s[. = '%s' or starts-with(., '%s') or contains(.,' %s ') ". "or substring(., string-length(.) - string-length('%s'))= ' %s']", $apfx, $valu, $valu, $valu, $valu } elsif ($cond->is_type(CLASS_CONDITION)) { # We assume all class='...' attributes are class # attributes and classes are separated by spaces sprintf "\@*[local-name() = 'class' and namespace-uri() = '']". "[. = '%s' or starts-with(., '%s ') or contains(., ' %s ') or ". "substring(., string-length(.) - string-length('%s')) = ' %s']", $valu, $valu, $valu, $valu } else { return } } sub CSS::SAC::Condition::Negative::ToXPath { my $cond = shift; my $args = shift || {}; # workaround for a CSS::SAC bug, it considers # :not(:foo) equivalent to :not(*:foo) @@@ my $not = $cond->Condition->isa('CSS::SAC::Selector::Conditional') ? $cond->Condition->Condition->ToXPath($args) : $cond->Condition->ToXPath($args); return unless defined $not; $not = "*" unless length $not; return "not(" . $not . ")"; } sub CSS::SAC::Condition::Positional::ToXPath { my $cond = shift; my $args = shift || {}; my $result = ""; my $epfx = $args->{parent}->_GetElement->ToXPath($args); $epfx = "[$epfx]" if length $epfx; # *|*:only-of-type cannot be expressed using XPath return if (not(length $epfx) or $epfx eq "[self::*]") and $cond->Type; $result .= "parent::* and " unless $cond->Type; my $p = $cond->Position; if ($p eq "1") { $result .= "not(preceding-sibling::*" . ($cond->Type ? "$epfx" : "") . ")" } elsif ($p eq "-1") { $result .= "not(following-sibling::*" . ($cond->Type ? "$epfx" : "") . ")" } elsif ($p =~ /^(?:-?(\d+)?n(?:\+(\d+))?)|odd|even|\d+$/x) { my ($x, $y, $n) = ($1, $2, undef); $n = $p =~ /^-/ ? '<' : '>'; ($x, $y) = (2, 0) if $p eq "even"; ($x, $y) = (2, 1) if $p eq "odd"; ($x, $y, $n) = ($p, $p, '<') if $p =~ /^\d+$/; $x = 1 unless defined $x; $y = 0 unless defined $y; my $test = "preceding-sibling::*"; $test .= "$epfx" if $cond->Type; $result .= "count($test) + 1 $n= $y and ((count($test) - $y + 1) mod $x) = 0"; } else { return } } sub CSS::SAC::Condition::Combinator::ToXPath { my $cond = shift; my $args = shift || {}; return unless $cond->is_type(AND_CONDITION); my $first = $cond->FirstCondition->ToXPath($args); my $second = $cond->SecondCondition->ToXPath($args); return unless defined $first and defined $second; return "$first and $second"; } sub CSS::SAC::Condition::ToXPath { my $cond = shift; my $args = shift || {}; if ($cond->is_type(IS_EMPTY_CONDITION)) { return "not(* or text())" } elsif ($cond->is_type(IS_ROOT_CONDITION)) { return "not(ancestor::*)" } elsif ($cond->is_type(ONLY_CHILD_CONDITION)) { return "not(preceding-sibling::*) and not(following-sibling::*)" } my $epfx = $args->{parent}->_GetElement->ToXPath($args); $epfx = "[$epfx]" if length $epfx; if ($cond->is_type(ONLY_TYPE_CONDITION)) { return "not(preceding-sibling::*$epfx or following-sibling::*$epfx)" } else { return } } sub CSS::SAC::Selector::Conditional::ToXPath { my $self = shift; my $args = shift || {}; my $result = ""; my $x = $self->SimpleSelector->ToXPath({ %$args, parent => $self }); my $y = $self->Condition->ToXPath({ %$args, parent => $self->SimpleSelector }); return unless defined $x and defined $y; $result .= $x; $result .= " and " if length $result; $result .= $y; return $result; } sub CSS::SAC::Selector::Descendant::ToXPath { my $self = shift; my $args = shift || {}; my $result = ""; my $x = $self->AncestorSelector->ToXPath({ %$args, parent => $self }); my $y = $self->SimpleSelector->ToXPath({ %$args, parent => $self }); return unless defined $x and defined $y; $result .= $self->is_type(CHILD_SELECTOR) ? "parent::*" : "ancestor::*"; $result .= "[$x]" if length $x; $result .= " and $y" if length $y; return $result; } sub CSS::SAC::Selector::Element::ToXPath { my $self = shift; my $result = ""; my $local = $self->LocalName; my $ns = $self->NamespaceURI; if (defined $local) { $result .= "local-name() = '$local'" } if (defined $local && defined $ns) { $result .= " and " } if (defined $ns) { $result .= "namespace-uri() = '$ns'" } $result = "self::*" unless length $result; return $result; } sub CSS::SAC::Selector::Sibling::ToXPath { my $self = shift; my $args = shift || {}; my $result = ""; my $x = $self->Selector->ToXPath({ %$args, parent => $self }); my $y = $self->SiblingSelector->ToXPath({ %$args, parent => $self }); return unless defined $x and defined $y; $result .= $self->is_type(DIRECT_ADJACENT_SELECTOR) ? "preceding-sibling::*[1]" : "preceding-sibling::*"; $result .= "[$x]" if length $x; $result .= " and $y" if length $y; return $result; } |