1 |
wakaba |
1.1 |
package SWE::DB::DOM; |
2 |
|
|
use strict; |
3 |
|
|
|
4 |
|
|
require Message::DOM::DOMImplementation; |
5 |
|
|
|
6 |
|
|
sub new ($) { |
7 |
|
|
my $self = bless { |
8 |
|
|
root_directory_name => './', |
9 |
|
|
root_key => ['HomePage'], |
10 |
|
|
leaf_suffix => '.txt', |
11 |
|
|
}, shift; |
12 |
|
|
return $self; |
13 |
|
|
} # new |
14 |
|
|
|
15 |
|
|
my $get_file_name = sub { |
16 |
|
|
my ($self, $key) = @_; |
17 |
|
|
$key = $self->{root_key} if @$key == 0; |
18 |
|
|
|
19 |
|
|
my $file_name = $self->{root_directory_name} . |
20 |
|
|
join '-', map { s/(.)/sprintf '%02X', ord $1/ges; $_ } @$key; |
21 |
|
|
$file_name .= $self->{leaf_suffix}; |
22 |
|
|
|
23 |
|
|
return $file_name; |
24 |
|
|
}; # $get_file_name |
25 |
|
|
|
26 |
|
|
sub get_data ($$) { |
27 |
|
|
my $self = $_[0]; |
28 |
|
|
my $key = [@{$_[1]}]; |
29 |
|
|
|
30 |
|
|
my $file_name = $get_file_name->($self, $key); |
31 |
|
|
|
32 |
|
|
unless (-f $file_name) { |
33 |
|
|
return undef; |
34 |
|
|
} |
35 |
|
|
|
36 |
|
|
open my $file, '<:encoding(utf8)', $file_name or die "$0: $file_name: $!"; |
37 |
|
|
return _load_dom ($file); |
38 |
|
|
} # get_data |
39 |
|
|
|
40 |
|
|
sub set_data ($$$) { |
41 |
|
|
my $self = $_[0]; |
42 |
|
|
my $key = [@{$_[1]}]; |
43 |
|
|
my $dom = $_[2]; |
44 |
|
|
|
45 |
|
|
my $file_name = $get_file_name->($self, $key); |
46 |
|
|
|
47 |
|
|
open my $file, '>:encoding(utf8)', $file_name or die "$0: $file_name: $!"; |
48 |
|
|
_store_dom ($file, $dom); |
49 |
|
|
} # set_data |
50 |
|
|
|
51 |
|
|
sub _load_dom ($) { |
52 |
|
|
my $handle = shift; |
53 |
|
|
|
54 |
|
|
my $dom = Message::DOM::DOMImplementation->new; |
55 |
|
|
my @node = ($dom->create_document); |
56 |
|
|
|
57 |
|
|
$node[0]->strict_error_checking (0); |
58 |
|
|
$node[0]->dom_config->set_parameter |
59 |
|
|
('http://suika.fam.cx/www/2006/dom-config/strict-document-children' => 0); |
60 |
|
|
|
61 |
|
|
my $unescape = sub { |
62 |
|
|
my $s = shift; |
63 |
|
|
$s =~ s/\\([0-9A-Fa-f]{2})/pack 'C', hex $1/ge; |
64 |
|
|
return $s; |
65 |
|
|
}; # $unescape |
66 |
|
|
|
67 |
|
|
while (<$handle>) { |
68 |
|
|
tr/\x0D\x0A//d; |
69 |
|
|
if (s/^n(\d+);//) { |
70 |
|
|
$node[$1] = $unescape->($_); |
71 |
|
|
} elsif (s/^t(\d+);//) { |
72 |
|
|
my $parent_id = $1; |
73 |
|
|
$node[$parent_id]->append_child |
74 |
|
|
($node[0]->create_text_node ($unescape->($_))); |
75 |
|
|
} elsif (s/^e(\d+);(\d+);(\d+);//) { |
76 |
|
|
my ($this_id, $parent_id, $ns_id) = ($1, $2, $3); |
77 |
|
|
$node[$this_id] = $node[0]->create_element_ns |
78 |
|
|
($node[$ns_id], [undef, $unescape->($_)]); |
79 |
|
|
$node[$parent_id]->append_child ($node[$this_id]); |
80 |
|
|
} elsif (s/^a(\d+);(\d+);([^;]+);//) { |
81 |
|
|
my ($parent_id, $ns_id, $ln) = ($1, $2, $3); |
82 |
|
|
$node[$parent_id]->set_attribute_ns |
83 |
|
|
($node[$ns_id], [undef, $unescape->($ln)], $unescape->($_)); |
84 |
|
|
} |
85 |
|
|
} |
86 |
|
|
|
87 |
|
|
return $node[0]; |
88 |
|
|
} # _load_dom |
89 |
|
|
|
90 |
|
|
sub _store_dom ($$) { |
91 |
|
|
my $handle = shift; |
92 |
|
|
my @item = ([0, shift]); |
93 |
|
|
|
94 |
|
|
my $escape = sub { |
95 |
|
|
my $v = $_[0]; |
96 |
|
|
$v =~ s/([;\\\x0D\x0A])/sprintf '\\%02X', ord $1/ge; |
97 |
|
|
return $v; |
98 |
|
|
}; # $escape |
99 |
|
|
|
100 |
|
|
my $ns; |
101 |
|
|
my $next_id = 1; |
102 |
|
|
while (@item) { |
103 |
|
|
my ($parent_id, $node) = @{shift @item}; |
104 |
|
|
if ($node->node_type == $node->ELEMENT_NODE) { |
105 |
|
|
my $nsuri = $node->namespace_uri // ''; |
106 |
|
|
my $nsid = $ns->{$nsuri}; |
107 |
|
|
unless (defined $nsid) { |
108 |
|
|
$nsid = $next_id++; |
109 |
|
|
$ns->{$nsuri} = $nsid; |
110 |
|
|
print $handle "n", $nsid, ';', $escape->($nsuri), "\n"; |
111 |
|
|
} |
112 |
|
|
my $el_id = $next_id++; |
113 |
|
|
print $handle "e", $el_id, ';', $parent_id, ';', $nsid, ';', |
114 |
|
|
$escape->($node->manakai_local_name), "\n"; |
115 |
|
|
for my $attr (@{$node->attributes}) { |
116 |
|
|
my $nsuri = $attr->namespace_uri // ''; |
117 |
|
|
my $nsid = $ns->{$nsuri}; |
118 |
|
|
unless (defined $nsid) { |
119 |
|
|
$nsid = $next_id++; |
120 |
|
|
$ns->{$nsuri} = $nsid; |
121 |
|
|
print $handle "n", $nsid, ';', $escape->($nsuri), "\n"; |
122 |
|
|
} |
123 |
|
|
print $handle "a", $el_id, ';', $nsid, ';', |
124 |
|
|
$escape->($attr->manakai_local_name), ';', |
125 |
|
|
$escape->($attr->value), "\n"; |
126 |
|
|
} |
127 |
|
|
unshift @item, map {[$el_id, $_]} @{$node->child_nodes}; |
128 |
|
|
} elsif ($node->node_type == $node->TEXT_NODE or |
129 |
|
|
$node->node_type == $node->CDATA_SECTION_NODE) { |
130 |
|
|
print $handle "t", $parent_id, ';', $escape->($node->data), "\n"; |
131 |
|
|
} elsif ($node->node_type == $node->DOCUMENT_NODE or |
132 |
|
|
$node->node_type == $node->DOCUMENT_FRAGMENT_NODE) { |
133 |
|
|
unshift @item, map {[0, $_]} @{$node->child_nodes}; |
134 |
|
|
} |
135 |
|
|
} |
136 |
|
|
} # _store_dom |
137 |
|
|
|
138 |
|
|
1; |