File Coverage

blib/lib/Apache/AxKit/Language/SpellCheck.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # $Id: SpellCheck.pm,v 1.5 2005/01/27 00:45:38 nachbaur Exp $
2              
3             package Apache::AxKit::Language::SpellCheck;
4              
5 1     1   1253 use base Apache::AxKit::Language;
  1         3  
  1         713  
6 1     1   7 use strict;
  1         2  
  1         42  
7              
8 1     1   1651 use AxKit;
  0            
  0            
9             use Apache;
10             use Apache::Request;
11             use Apache::AxKit::Language;
12             use Apache::AxKit::Provider;
13             use Text::Aspell;
14             use Cwd;
15              
16             our $VERSION = 0.03;
17             our $NS = 'http://axkit.org/2004/07/17-spell-check#';
18              
19             sub stylesheet_exists () { 0; }
20              
21             sub handler {
22             my $class = shift;
23             my ($r, $xml_provider, undef, $last_in_chain) = @_;
24            
25             #
26             # Create and init the speller object
27             my $spell = new Text::Aspell;
28             $spell->set_option('sug-mode', 'fast');
29             $spell->set_option('lang', $r->dir_config("AxSpellLanguage") || 'en_US');
30             my $max_suggestion = $r->dir_config("AxSpellSuggestions") || 3;
31              
32             #
33             # Process the list of elements we need to skip
34             my %skip_elements = ();
35             foreach my $element (split(/\s+/, $r->dir_config("AxSpellSkipElements") )) {
36             if ($element !~ /^(?:\{(.*?)\})?([\w\d\-\_]+)$/) {
37             die "The element \"$element\" is invalid in AxSpellSkipElements";
38             }
39             my $ns = $1;
40             my $node = $2;
41             $skip_elements{$ns}->{$node} = 1;
42             }
43              
44             #
45             # Load the DOM object
46             my $dom = $r->pnotes('dom_tree');
47             unless ($dom) {
48             my $xmlstring = $r->pnotes('xml_string');
49             my $parser = XML::LibXML->new();
50             $parser->expand_entities(1);
51             $dom = $parser->parse_string($xmlstring, $r->uri());
52             }
53              
54             #
55             # Find the root node
56             my $root = $dom->documentElement();
57             $root->setNamespace($NS, 'sp', 0);
58              
59             #
60             # Iterate through all the text nodes
61             foreach my $text_node ($root->findnodes('//text()')) {
62              
63             #
64             # Skip if our parent is in the exclude list
65             my $parent = $text_node->parentNode;
66             if ($skip_elements{$parent->namespaceURI}->{$parent->localname}) {
67             #warn "Skipping " . $text_node->data . " due to parent " . $parent->nodeName . "\n";
68             next;
69             }
70              
71             my @nodes = ();
72             my $pre_text = undef;
73             my $changed = 0;
74              
75             #
76             # Loop through the words in this text ndoe
77             foreach my $word (split(/\b/, $text_node->data)) {
78              
79             #
80             # Skip empty strings and non-spellable words
81             next unless defined $word;
82             unless ($word =~ /^\p{L}+$/i) {
83             $pre_text .= $word;
84             next;
85             }
86              
87             #
88             # Check the word against the spellchecker
89             if ($spell->check($word)) {
90             $pre_text .= $word;
91             }
92            
93             #
94             # The word isn't spelled right, add elements
95             else {
96             $changed++;
97              
98             #
99             # Add an initial text node if the unspelled word is somewhere in the middle
100             push @nodes, XML::LibXML::Text->new($pre_text) if (length($pre_text));
101             $pre_text = undef;
102              
103             #
104             # Add the root element for this spelling block
105             my $element = $dom->createElementNS($NS, "incorrect");
106              
107             #
108             # Iterate and add our suggestions
109             my $counter = 0;
110             if ($max_suggestion) {
111             foreach my $suggestion ($spell->suggest($word)) {
112             #
113             # Add the suggestion element
114             my $suggest_node = $dom->createElementNS($NS, "suggestion");
115             $suggest_node->appendText($suggestion);
116             $element->appendChild($suggest_node);
117             last if (++$counter > $max_suggestion);
118             }
119             }
120              
121             #
122             # Add the element for the current, misspelled word
123             my $word_node = $dom->createElementNS($NS, "word");
124             $word_node->appendText($word);
125             $element->appendChild($word_node);
126             push @nodes, $element;
127             }
128             }
129              
130             #
131             # Wrap up any text thats left over as a text node
132             push @nodes, XML::LibXML::Text->new($pre_text) if (length($pre_text));
133              
134             #
135             # If nothing's changed, don't bother changing the DOM
136             next unless ($changed);
137              
138             my $parent = $text_node->parentNode;
139              
140             #
141             # If we have multiple nodes to add, add them
142             if ($#nodes > 0) {
143             #
144             # Replace the current text with the first node we have
145             my $first_node = shift(@nodes);
146             $parent->replaceChild($first_node, $text_node);
147              
148             #
149             # Iterate through the additional nodes, and append them to
150             # the previously-added node
151             my $previous_node = $first_node;
152             foreach my $node (@nodes) {
153             $parent->insertAfter($node, $previous_node);
154             $previous_node = $node;
155             }
156             }
157            
158             #
159             # Since there's only one element to replace, just swap it out; its simpler
160             else {
161             $parent->replaceChild($nodes[0], $text_node);
162             }
163             }
164              
165             #
166             # Return the current dom document
167             delete $r->pnotes()->{'xml_string'};
168             $r->pnotes('dom_tree', $dom);
169            
170             return Apache::Constants::OK;
171             }
172              
173             1;
174             __END__