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__ |