| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# ----------------- Stone --------------- |
|
2
|
|
|
|
|
|
|
# This is basic unit of the boulder stream, and defines a |
|
3
|
|
|
|
|
|
|
# multi-valued hash array type of structure. |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Stone; |
|
6
|
2
|
|
|
2
|
|
12
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
98
|
|
|
7
|
2
|
|
|
2
|
|
11
|
use vars qw($VERSION $AUTOLOAD $Fetchlast); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
186
|
|
|
8
|
2
|
|
|
|
|
13
|
use overload '""' => 'toString', |
|
9
|
2
|
|
|
2
|
|
4370
|
'fallback' =>' TRUE'; |
|
|
2
|
|
|
|
|
2410
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
$VERSION = '1.30'; |
|
12
|
|
|
|
|
|
|
require 5.004; |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 NAME |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
Stone - In-memory storage for hierarchical tag/value data structures |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
use Stone; |
|
21
|
|
|
|
|
|
|
my $stone = Stone->new( Jim => { First_name => 'James', |
|
22
|
|
|
|
|
|
|
Last_name => 'Hill', |
|
23
|
|
|
|
|
|
|
Age => 34, |
|
24
|
|
|
|
|
|
|
Address => { |
|
25
|
|
|
|
|
|
|
Street => ['The Manse', |
|
26
|
|
|
|
|
|
|
'19 Chestnut Ln'], |
|
27
|
|
|
|
|
|
|
City => 'Garden City', |
|
28
|
|
|
|
|
|
|
State => 'NY', |
|
29
|
|
|
|
|
|
|
Zip => 11291 } |
|
30
|
|
|
|
|
|
|
}, |
|
31
|
|
|
|
|
|
|
Sally => { First_name => 'Sarah', |
|
32
|
|
|
|
|
|
|
Last_name => 'James', |
|
33
|
|
|
|
|
|
|
Age => 30, |
|
34
|
|
|
|
|
|
|
Address => { |
|
35
|
|
|
|
|
|
|
Street => 'Hickory Street', |
|
36
|
|
|
|
|
|
|
City => 'Katonah', |
|
37
|
|
|
|
|
|
|
State => 'NY', |
|
38
|
|
|
|
|
|
|
Zip => 10578 } |
|
39
|
|
|
|
|
|
|
} |
|
40
|
|
|
|
|
|
|
); |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
@tags = $stone->tags; # yields ('James','Sally'); |
|
43
|
|
|
|
|
|
|
$address = $stone->Jim->Address; # gets the address subtree |
|
44
|
|
|
|
|
|
|
@street = $address->Street; # yeilds ('The Manse','19 Chestnut Ln') |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
$address = $stone->get('Jim')->get('Address'); # same as $stone->Jim->Address |
|
47
|
|
|
|
|
|
|
$address = $stone->get('Jim.Address'); # another way to express same thing |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# first Street tag in Jim's address |
|
50
|
|
|
|
|
|
|
$address = $stone->get('Jim.Address.Street[0]'); |
|
51
|
|
|
|
|
|
|
# second Street tag in Jim's address |
|
52
|
|
|
|
|
|
|
$address = $stone->get('Jim.Address.Street[1]'); |
|
53
|
|
|
|
|
|
|
# last Street tag in Jim's address |
|
54
|
|
|
|
|
|
|
$address = $stone->get('Jim.Address.Street[#]'); |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# insert a tag/value pair |
|
57
|
|
|
|
|
|
|
$stone->insert(Martha => { First_name => 'Martha', Last_name => 'Steward'} ); |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# find the first Address |
|
60
|
|
|
|
|
|
|
$stone->search('Address'); |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# change an existing subtree |
|
63
|
|
|
|
|
|
|
$martha = $stone->Martha; |
|
64
|
|
|
|
|
|
|
$martha->replace(Last_name => 'Stewart'); # replace a value |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# iterate over the tree with a cursor |
|
67
|
|
|
|
|
|
|
$cursor = $stone->cursor; |
|
68
|
|
|
|
|
|
|
while (my ($key,$value) = $cursor->each) { |
|
69
|
|
|
|
|
|
|
print "$value: Go Bluejays!\n" if $key eq 'State' and $value eq 'Katonah'; |
|
70
|
|
|
|
|
|
|
} |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# various format conversions |
|
73
|
|
|
|
|
|
|
print $stone->asTable; |
|
74
|
|
|
|
|
|
|
print $stone->asString; |
|
75
|
|
|
|
|
|
|
print $stone->asHTML; |
|
76
|
|
|
|
|
|
|
print $stone->asXML('Person'); |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
A L consists of a series of tag/value pairs. Any given tag may |
|
81
|
|
|
|
|
|
|
be single-valued or multivalued. A value can be another Stone, |
|
82
|
|
|
|
|
|
|
allowing nested components. A big Stone can be made up of a lot of |
|
83
|
|
|
|
|
|
|
little stones (pebbles?). You can obtain a Stone from a |
|
84
|
|
|
|
|
|
|
L or L persistent database. |
|
85
|
|
|
|
|
|
|
Alternatively you can build your own Stones bit by bit. |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Stones can be exported into string, XML and HTML representations. In |
|
88
|
|
|
|
|
|
|
addition, they are flattened into a linearized representation when |
|
89
|
|
|
|
|
|
|
reading from or writing to a L or one of its |
|
90
|
|
|
|
|
|
|
descendents. |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
L was designed for subclassing. You should be able to create |
|
93
|
|
|
|
|
|
|
subclasses which create or require particular tags and data formats. |
|
94
|
|
|
|
|
|
|
Currently only L subclasses L. |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head1 CONSTRUCTORS |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Stones are either created by calling the new() method, or by reading |
|
99
|
|
|
|
|
|
|
them from a L or persistent database. |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=head2 $stone = Stone->new() |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
This is the main constructor for the Stone class. It can be called |
|
104
|
|
|
|
|
|
|
without any parameters, in which case it creates an empty Stone object |
|
105
|
|
|
|
|
|
|
(no tags or values), or it may passed an associative array in order to |
|
106
|
|
|
|
|
|
|
initialize it with a set of tags. A tag's value may be a scalar, an |
|
107
|
|
|
|
|
|
|
anonymous array reference (constructed using [] brackets), or a hash |
|
108
|
|
|
|
|
|
|
references (constructed using {} brackets). In the first case, the |
|
109
|
|
|
|
|
|
|
tag will be single-valued. In the second, the tag will be |
|
110
|
|
|
|
|
|
|
multivalued. In the third case, a subsidiary Stone will be generated |
|
111
|
|
|
|
|
|
|
automatically and placed into the tree at the specified location. |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Examples: |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
$myStone = new Stone; |
|
116
|
|
|
|
|
|
|
$myStone = new Stone(Name=>'Fred',Age=>30); |
|
117
|
|
|
|
|
|
|
$myStone = new Stone(Name=>'Fred', |
|
118
|
|
|
|
|
|
|
Friend=>['Jill','John','Jerry']); |
|
119
|
|
|
|
|
|
|
$myStone = new Stone(Name=>'Fred', |
|
120
|
|
|
|
|
|
|
Friend=>['Jill', |
|
121
|
|
|
|
|
|
|
'John', |
|
122
|
|
|
|
|
|
|
'Gerald' |
|
123
|
|
|
|
|
|
|
], |
|
124
|
|
|
|
|
|
|
Attributes => { Hair => 'blonde', |
|
125
|
|
|
|
|
|
|
Eyes => 'blue' } |
|
126
|
|
|
|
|
|
|
); |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
In the last example, a Stone with the following structure is created: |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Name Fred |
|
131
|
|
|
|
|
|
|
Friend Jill |
|
132
|
|
|
|
|
|
|
Friend John |
|
133
|
|
|
|
|
|
|
Friend Gerald |
|
134
|
|
|
|
|
|
|
Attributes Eyes blue |
|
135
|
|
|
|
|
|
|
Hair blonde |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
Note that the value corresponding to the tag "Attributes" is itself a |
|
138
|
|
|
|
|
|
|
Stone with two tags, "Eyes" and "Hair". |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
The XML representation (which could be created with asXML()) looks like this: |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
blue |
|
146
|
|
|
|
|
|
|
blonde |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
Jill |
|
149
|
|
|
|
|
|
|
John |
|
150
|
|
|
|
|
|
|
Gerald |
|
151
|
|
|
|
|
|
|
Fred |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
More information on Stone initialization is given in the description |
|
155
|
|
|
|
|
|
|
of the insert() method. |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head1 OBJECT METHODS |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
Once a Stone object is created or retrieved, you can manipulate it |
|
160
|
|
|
|
|
|
|
with the following methods. |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head2 $stone->insert(%hash) |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=head2 $stone->insert(\%hash) |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
This is the main method for adding tags to a Stone. This method |
|
167
|
|
|
|
|
|
|
expects an associative array as an argument or a reference to one. |
|
168
|
|
|
|
|
|
|
The contents of the associative array will be inserted into the Stone. |
|
169
|
|
|
|
|
|
|
If a particular tag is already present in the Stone, the tag's current |
|
170
|
|
|
|
|
|
|
value will be appended to the list of values for that tag. Several |
|
171
|
|
|
|
|
|
|
types of values are legal: |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=over 4 |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=item * A B value |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
The value will be inserted into the C. |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
$stone->insert(name=>Fred, |
|
180
|
|
|
|
|
|
|
age=>30, |
|
181
|
|
|
|
|
|
|
sex=>M); |
|
182
|
|
|
|
|
|
|
$stone->dump; |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
name[0]=Fred |
|
185
|
|
|
|
|
|
|
age[0]=30 |
|
186
|
|
|
|
|
|
|
sex[0]=M |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=item * An B reference |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
A multi-valued tag will be created: |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
$stone->insert(name=>Fred, |
|
193
|
|
|
|
|
|
|
children=>[Tom,Mary,Angelique]); |
|
194
|
|
|
|
|
|
|
$stone->dump; |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
name[0]=Fred |
|
197
|
|
|
|
|
|
|
children[0]=Tom |
|
198
|
|
|
|
|
|
|
children[1]=Mary |
|
199
|
|
|
|
|
|
|
children[2]=Angelique |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=item * A B reference |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
A subsidiary C object will be created and inserted into the |
|
204
|
|
|
|
|
|
|
object as a nested structure. |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
$stone->insert(name=>Fred, |
|
207
|
|
|
|
|
|
|
wife=>{name=>Agnes,age=>40}); |
|
208
|
|
|
|
|
|
|
$stone->dump; |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
name[0]=Fred |
|
211
|
|
|
|
|
|
|
wife[0].name[0]=Agnes |
|
212
|
|
|
|
|
|
|
wife[0].age[0]=40 |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=item * A C object or subclass |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
The C object will be inserted into the object as a nested |
|
217
|
|
|
|
|
|
|
structure. |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
$wife = new Stone(name=>agnes, |
|
220
|
|
|
|
|
|
|
age=>40); |
|
221
|
|
|
|
|
|
|
$husband = new Stone; |
|
222
|
|
|
|
|
|
|
$husband->insert(name=>fred, |
|
223
|
|
|
|
|
|
|
wife=>$wife); |
|
224
|
|
|
|
|
|
|
$husband->dump; |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
name[0]=fred |
|
227
|
|
|
|
|
|
|
wife[0].name[0]=agnes |
|
228
|
|
|
|
|
|
|
wife[0].age[0]=40 |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=back |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=head2 $stone->replace(%hash) |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 $stone->replace(\%hash) |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
The B method behaves exactly like C with the |
|
237
|
|
|
|
|
|
|
exception that if the indicated key already exists in the B, |
|
238
|
|
|
|
|
|
|
its value will be replaced. Use B when you want to enforce |
|
239
|
|
|
|
|
|
|
a single-valued tag/value relationship. |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=head2 $stone->insert_list($key,@list) |
|
242
|
|
|
|
|
|
|
=head2 $stone->insert_hash($key,%hash) |
|
243
|
|
|
|
|
|
|
=head2 $stone->replace_list($key,@list) |
|
244
|
|
|
|
|
|
|
=head2 $stone->replace_hash($key,%hash) |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
These are primitives used by the C and C methods. |
|
247
|
|
|
|
|
|
|
Override them if you need to modify the default behavior. |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=head2 $stone->delete($tag) |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
This removes the indicated tag from the Stone. |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=head2 @values = $stone->get($tag [,$index]) |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
This returns the value at the indicated tag and optional index. What |
|
256
|
|
|
|
|
|
|
you get depends on whether it is called in a scalar or list context. |
|
257
|
|
|
|
|
|
|
In a list context, you will receive all the values for that tag. You |
|
258
|
|
|
|
|
|
|
may receive a list of scalar values or (for a nested record) or a list |
|
259
|
|
|
|
|
|
|
of Stone objects. If called in a scalar context, you will either |
|
260
|
|
|
|
|
|
|
receive the first or the last member of the list of values assigned to |
|
261
|
|
|
|
|
|
|
the tag. Which one you receive depends on the value of the package |
|
262
|
|
|
|
|
|
|
variable C<$Stone::Fetchlast>. If undefined, you will receive the |
|
263
|
|
|
|
|
|
|
first member of the list. If nonzero, you will receive the last |
|
264
|
|
|
|
|
|
|
member. |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
You may provide an optional index in order to force get() to return a |
|
267
|
|
|
|
|
|
|
particular member of the list. Provide a 0 to return the first member |
|
268
|
|
|
|
|
|
|
of the list, or '#' to obtain the last member. |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
If the tag contains a period (.), get() will call index() on your |
|
271
|
|
|
|
|
|
|
behalf (see below). |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
If the tag begins with an uppercase letter, then you can use the |
|
274
|
|
|
|
|
|
|
autogenerated method to access it: |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
$stone->Tag_name([$index]) |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
This is exactly equivalent to: |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
$stone->get('Teg_name' [,$index]) |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=head2 @values = $stone->search($tag) |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Searches for the first occurrence of the tag, traversing the tree in a |
|
285
|
|
|
|
|
|
|
breadth-first manner, and returns it. This allows you to retrieve the |
|
286
|
|
|
|
|
|
|
value of a tag in a deeply nested structure without worrying about all |
|
287
|
|
|
|
|
|
|
the intermediate nodes. For example: |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
$myStone = new Stone(Name=>'Fred', |
|
290
|
|
|
|
|
|
|
Friend=>['Jill', |
|
291
|
|
|
|
|
|
|
'John', |
|
292
|
|
|
|
|
|
|
'Gerald' |
|
293
|
|
|
|
|
|
|
], |
|
294
|
|
|
|
|
|
|
Attributes => { Hair => 'blonde', |
|
295
|
|
|
|
|
|
|
Eyes => 'blue' } |
|
296
|
|
|
|
|
|
|
); |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
$hair_colour = $stone->search('Hair'); |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
The disadvantage of this is that if there is a tag named "Hair" higher |
|
301
|
|
|
|
|
|
|
in the hierarchy, this tag will be retrieved rather than the lower |
|
302
|
|
|
|
|
|
|
one. In an array context this method returns the complete list of |
|
303
|
|
|
|
|
|
|
values from the matching tag. In a scalar context, it returns either |
|
304
|
|
|
|
|
|
|
the first or the last value of multivalued tags depending as usual on |
|
305
|
|
|
|
|
|
|
the value of C<$Stone::Fetchlast>. |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
C<$Stone::Fetchlast> is also consulted during the depth-first |
|
308
|
|
|
|
|
|
|
traversal. If C<$Fetchlast> is set to a true value, multivalued |
|
309
|
|
|
|
|
|
|
intermediate tags will be searched from the last to the first rather |
|
310
|
|
|
|
|
|
|
than the first to the last. |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
The Stone object has an AUTOLOAD method that invokes get() when you |
|
313
|
|
|
|
|
|
|
call a method that is not predefined. This allows a very convenient |
|
314
|
|
|
|
|
|
|
type of shortcut: |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
$name = $stone->Name; |
|
317
|
|
|
|
|
|
|
@friends = $stone->Friend; |
|
318
|
|
|
|
|
|
|
$eye_color = $stone->Attributes->Eyes |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
In the first example, we retrieve the value of the top-level tag Name. |
|
321
|
|
|
|
|
|
|
In the second example, we retrieve the value of the Friend tag.. In |
|
322
|
|
|
|
|
|
|
the third example, we retrieve the attributes stone first, then the |
|
323
|
|
|
|
|
|
|
Eyes value. |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
NOTE: By convention, methods are only autogenerated for tags that |
|
326
|
|
|
|
|
|
|
begin with capital letters. This is necessary to avoid conflict with |
|
327
|
|
|
|
|
|
|
hard-coded methods, all of which are lower case. |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=head2 @values = $stone->index($indexstr) |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
You can access the contents of even deeply-nested B objects |
|
332
|
|
|
|
|
|
|
with the C method. You provide a B, and receive |
|
333
|
|
|
|
|
|
|
a value or list of values back. |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Tag paths look like this: |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
tag1[index1].tag2[index2].tag3[index3] |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Numbers in square brackets indicate which member of a multivalued tag |
|
340
|
|
|
|
|
|
|
you're interested in getting. You can leave the square brackets out |
|
341
|
|
|
|
|
|
|
in order to return just the first or the last tag of that name, in a scalar |
|
342
|
|
|
|
|
|
|
context (depending on the setting of B<$Stone::Fetchlast>). In an |
|
343
|
|
|
|
|
|
|
array context, leaving the square brackets out will return B |
|
344
|
|
|
|
|
|
|
multivalued members for each tag along the path. |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
You will get a scalar value in a scalar context and an array value in |
|
347
|
|
|
|
|
|
|
an array context following the same rules as B. You can |
|
348
|
|
|
|
|
|
|
provide an index of '#' in order to get the last member of a list or |
|
349
|
|
|
|
|
|
|
a [?] to obtain a randomly chosen member of the list (this uses the rand() call, |
|
350
|
|
|
|
|
|
|
so be sure to call srand() at the beginning of your program in order |
|
351
|
|
|
|
|
|
|
to get different sequences of pseudorandom numbers. If |
|
352
|
|
|
|
|
|
|
there is no tag by that name, you will receive undef or an empty list. |
|
353
|
|
|
|
|
|
|
If the tag points to a subrecord, you will receive a B object. |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Examples: |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# Here's what the data structure looks like. |
|
358
|
|
|
|
|
|
|
$s->insert(person=>{name=>Fred, |
|
359
|
|
|
|
|
|
|
age=>30, |
|
360
|
|
|
|
|
|
|
pets=>[Fido,Rex,Lassie], |
|
361
|
|
|
|
|
|
|
children=>[Tom,Mary]}, |
|
362
|
|
|
|
|
|
|
person=>{name=>Harry, |
|
363
|
|
|
|
|
|
|
age=>23, |
|
364
|
|
|
|
|
|
|
pets=>[Rover,Spot]}); |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# Return all of Fred's children |
|
367
|
|
|
|
|
|
|
@children = $s->index('person[0].children'); |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# Return Harry's last pet |
|
370
|
|
|
|
|
|
|
$pet = $s->index('person[1].pets[#]'); |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
# Return first person's first child |
|
373
|
|
|
|
|
|
|
$child = $s->index('person.children'); |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
# Return children of all person's |
|
376
|
|
|
|
|
|
|
@children = $s->index('person.children'); |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# Return last person's last pet |
|
379
|
|
|
|
|
|
|
$Stone::Fetchlast++; |
|
380
|
|
|
|
|
|
|
$pet = $s->index('person.pets'); |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# Return any pet from any person |
|
383
|
|
|
|
|
|
|
$pet = $s->index('person[?].pet[?]'); |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
I that B may return a B object if the tag path |
|
386
|
|
|
|
|
|
|
points to a subrecord. |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=head2 $array = $stone->at($tag) |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
This returns an ARRAY REFERENCE for the tag. It is useful to prevent |
|
391
|
|
|
|
|
|
|
automatic dereferencing. Use with care. It is equivalent to: |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
$stone->{'tag'} |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
at() will always return an array reference. Single-valued tags will |
|
396
|
|
|
|
|
|
|
return a reference to an array of size 1. |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=head2 @tags = $stone->tags() |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
Return all the tags in the Stone. You can then use this list with |
|
401
|
|
|
|
|
|
|
get() to retrieve values or recursively traverse the stone. |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
=head2 $string = $stone->asTable() |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
Return the data structure as a tab-delimited table suitable for |
|
406
|
|
|
|
|
|
|
printing. |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=head2 $string = $stone->asXML([$tagname]) |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
Return the data structure in XML format. The entire data structure |
|
411
|
|
|
|
|
|
|
will be placed inside a top-level tag called . If you wish to |
|
412
|
|
|
|
|
|
|
change this top-level tag, pass it as an argument to asXML(). |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
An example follows: |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
print $stone->asXML('Address_list'); |
|
417
|
|
|
|
|
|
|
# yields: |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
10578 |
|
424
|
|
|
|
|
|
|
Katonah |
|
425
|
|
|
|
|
|
|
Hickory Street |
|
426
|
|
|
|
|
|
|
NY |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
Smith |
|
429
|
|
|
|
|
|
|
30 |
|
430
|
|
|
|
|
|
|
Sarah |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
11291 |
|
435
|
|
|
|
|
|
|
Garden City |
|
436
|
|
|
|
|
|
|
The Manse |
|
437
|
|
|
|
|
|
|
19 Chestnut Ln |
|
438
|
|
|
|
|
|
|
NY |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
Hill |
|
441
|
|
|
|
|
|
|
34 |
|
442
|
|
|
|
|
|
|
James |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head2 $hash = $stone->attributes([$att_name, [$att_value]]]) |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
attributes() returns the "attributes" of a tag. Attributes are a |
|
449
|
|
|
|
|
|
|
series of unique tag/value pairs which are associated with a tag, but |
|
450
|
|
|
|
|
|
|
are not contained within it. Attributes can only be expressed in the |
|
451
|
|
|
|
|
|
|
XML representation of a Stone: |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
10578 |
|
456
|
|
|
|
|
|
|
Katonah |
|
457
|
|
|
|
|
|
|
Hickory Street |
|
458
|
|
|
|
|
|
|
NY |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
Called with no arguments, attributes() returns the current attributes |
|
463
|
|
|
|
|
|
|
as a hash ref: |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
my $att = $stone->Address->attributes; |
|
466
|
|
|
|
|
|
|
my $type = $att->{type}; |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
Called with a single argument, attributes() returns the value of the |
|
469
|
|
|
|
|
|
|
named attribute, or undef if not defined: |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
my $type = $stone->Address->attributes('type'); |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
Called with two arguments, attributes() sets the named attribute: |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
my $type = $stone->Address->attributes(type => 'Rural Free Delivery'); |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
You may also change all attributes in one fell swoop by passing a hash |
|
478
|
|
|
|
|
|
|
reference as the single argument: |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
$stone->attributes({id=>'Sally Mae',version=>'2.1'}); |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=head2 $string = $stone->toString() |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
toString() returns a simple version of the Stone that shows just the |
|
485
|
|
|
|
|
|
|
topmost tags and the number of each type of tag. For example: |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
print $stone->Jim->Address; |
|
488
|
|
|
|
|
|
|
#yields => Zip(1),City(1),Street(2),State(1) |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
This method is used internally for string interpolation. If you try |
|
491
|
|
|
|
|
|
|
to print or otherwise manipulate a Stone object as a string, you will |
|
492
|
|
|
|
|
|
|
obtain this type of string as a result. |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=head2 $string = $stone->asHTML([\&callback]) |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Return the data structure as a nicely-formatted HTML 3.2 table, |
|
497
|
|
|
|
|
|
|
suitable for display in a Web browser. You may pass this method a |
|
498
|
|
|
|
|
|
|
callback routine which will be called for every tag/value pair in the |
|
499
|
|
|
|
|
|
|
object. It will be passed a two-item list containing the current tag |
|
500
|
|
|
|
|
|
|
and value. It can make any modifications it likes and return the |
|
501
|
|
|
|
|
|
|
modified tag and value as a return result. You can use this to modify |
|
502
|
|
|
|
|
|
|
tags or values on the fly, for example to turn them into HTML links. |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
For example, this code fragment will turn all tags named "Sequence" |
|
505
|
|
|
|
|
|
|
blue: |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
my $callback = sub { |
|
508
|
|
|
|
|
|
|
my ($tag,$value) = @_; |
|
509
|
|
|
|
|
|
|
return ($tag,$value) unless $tag eq 'Sequence'; |
|
510
|
|
|
|
|
|
|
return ( qq($tag),$value ); |
|
511
|
|
|
|
|
|
|
} |
|
512
|
|
|
|
|
|
|
print $stone->asHTML($callback); |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=head2 Stone::dump() |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
This is a debugging tool. It iterates through the B object and |
|
517
|
|
|
|
|
|
|
prints out all the tags and values. |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
Example: |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
$s->dump; |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
person[0].children[0]=Tom |
|
524
|
|
|
|
|
|
|
person[0].children[1]=Mary |
|
525
|
|
|
|
|
|
|
person[0].name[0]=Fred |
|
526
|
|
|
|
|
|
|
person[0].pets[0]=Fido |
|
527
|
|
|
|
|
|
|
person[0].pets[1]=Rex |
|
528
|
|
|
|
|
|
|
person[0].pets[2]=Lassie |
|
529
|
|
|
|
|
|
|
person[0].age[0]=30 |
|
530
|
|
|
|
|
|
|
person[1].name[0]=Harry |
|
531
|
|
|
|
|
|
|
person[1].pets[0]=Rover |
|
532
|
|
|
|
|
|
|
person[1].pets[1]=Spot |
|
533
|
|
|
|
|
|
|
person[1].age[0]=23 |
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
=head2 $cursor = $stone->cursor() |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
Retrieves an iterator over the object. You can call this several |
|
538
|
|
|
|
|
|
|
times in order to return independent iterators. The following brief |
|
539
|
|
|
|
|
|
|
example is described in more detail in L. |
|
540
|
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
my $curs = $stone->cursor; |
|
542
|
|
|
|
|
|
|
while (my($tag,$value) = $curs->next_pair) { |
|
543
|
|
|
|
|
|
|
print "$tag => $value\n"; |
|
544
|
|
|
|
|
|
|
} |
|
545
|
|
|
|
|
|
|
# yields: |
|
546
|
|
|
|
|
|
|
Sally[0].Address[0].Zip[0] => 10578 |
|
547
|
|
|
|
|
|
|
Sally[0].Address[0].City[0] => Katonah |
|
548
|
|
|
|
|
|
|
Sally[0].Address[0].Street[0] => Hickory Street |
|
549
|
|
|
|
|
|
|
Sally[0].Address[0].State[0] => NY |
|
550
|
|
|
|
|
|
|
Sally[0].Last_name[0] => James |
|
551
|
|
|
|
|
|
|
Sally[0].Age[0] => 30 |
|
552
|
|
|
|
|
|
|
Sally[0].First_name[0] => Sarah |
|
553
|
|
|
|
|
|
|
Jim[0].Address[0].Zip[0] => 11291 |
|
554
|
|
|
|
|
|
|
Jim[0].Address[0].City[0] => Garden City |
|
555
|
|
|
|
|
|
|
Jim[0].Address[0].Street[0] => The Manse |
|
556
|
|
|
|
|
|
|
Jim[0].Address[0].Street[1] => 19 Chestnut Ln |
|
557
|
|
|
|
|
|
|
Jim[0].Address[0].State[0] => NY |
|
558
|
|
|
|
|
|
|
Jim[0].Last_name[0] => Hill |
|
559
|
|
|
|
|
|
|
Jim[0].Age[0] => 34 |
|
560
|
|
|
|
|
|
|
Jim[0].First_name[0] => James |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=head1 AUTHOR |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
Lincoln D. Stein . |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
Copyright 1997-1999, Cold Spring Harbor Laboratory, Cold Spring Harbor |
|
569
|
|
|
|
|
|
|
NY. This module can be used and distributed on the same terms as Perl |
|
570
|
|
|
|
|
|
|
itself. |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
L, L, L, L, |
|
575
|
|
|
|
|
|
|
L, L |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
=cut |
|
578
|
|
|
|
|
|
|
|
|
579
|
2
|
|
|
2
|
|
3124
|
use Stone::Cursor; |
|
|
2
|
|
|
|
|
7
|
|
|
|
2
|
|
|
|
|
66
|
|
|
580
|
2
|
|
|
2
|
|
12
|
use Carp; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
254
|
|
|
581
|
2
|
|
|
2
|
|
11
|
use constant DEFAULT_WIDTH=>25; # column width for pretty-printing |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
13734
|
|
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
# This global controls whether you will get the first or the |
|
584
|
|
|
|
|
|
|
# last member of a multi-valued attribute when you invoke |
|
585
|
|
|
|
|
|
|
# get() in a scalar context. |
|
586
|
|
|
|
|
|
|
$Stone::Fetchlast=0; |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
sub AUTOLOAD { |
|
589
|
0
|
|
|
0
|
|
0
|
my($pack,$func_name) = $AUTOLOAD=~/(.+)::([^:]+)$/; |
|
590
|
0
|
|
|
|
|
0
|
my $self = shift; |
|
591
|
0
|
0
|
|
|
|
0
|
croak "Can't locate object method \"$func_name\" via package \"$pack\". ", |
|
592
|
|
|
|
|
|
|
"Tag names must begin with a capital letter in order to be called this way" |
|
593
|
|
|
|
|
|
|
unless $func_name =~ /^[A-Z]/; |
|
594
|
0
|
|
|
|
|
0
|
return $self->get($func_name,@_); |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# Create a new Stone object, filling it with the |
|
598
|
|
|
|
|
|
|
# provided tag/value pairs, if any |
|
599
|
|
|
|
|
|
|
sub new { |
|
600
|
38
|
|
|
38
|
1
|
56
|
my($pack,%initial_values) = @_; |
|
601
|
38
|
|
|
|
|
62
|
my($self) = bless {},$pack; |
|
602
|
38
|
100
|
|
|
|
86
|
$self->insert(%initial_values) if %initial_values; |
|
603
|
38
|
|
|
|
|
71
|
return $self; |
|
604
|
|
|
|
|
|
|
} |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# Insert the key->value pairs into the Stone object, |
|
607
|
|
|
|
|
|
|
# appending to any similarly-named keys that were there before. |
|
608
|
|
|
|
|
|
|
sub insert { |
|
609
|
21
|
|
|
21
|
1
|
48
|
my($self,@arg) = @_; |
|
610
|
|
|
|
|
|
|
|
|
611
|
21
|
|
|
|
|
26
|
my %hash; |
|
612
|
21
|
50
|
33
|
|
|
48
|
if (ref $arg[0] and ref $arg[0] eq 'HASH') { |
|
613
|
0
|
|
|
|
|
0
|
%hash = %{$arg[0]}; |
|
|
0
|
|
|
|
|
0
|
|
|
614
|
|
|
|
|
|
|
} else { |
|
615
|
21
|
|
|
|
|
42
|
%hash = @arg; |
|
616
|
|
|
|
|
|
|
} |
|
617
|
|
|
|
|
|
|
|
|
618
|
21
|
|
|
|
|
39
|
foreach (keys %hash) { |
|
619
|
25
|
|
|
|
|
49
|
$self->insert_list($_,$hash{$_}); |
|
620
|
|
|
|
|
|
|
} |
|
621
|
|
|
|
|
|
|
} |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
# Add the key->value pairs to the Stone object, |
|
624
|
|
|
|
|
|
|
# replacing any similarly-named keys that were there before. |
|
625
|
|
|
|
|
|
|
sub replace { |
|
626
|
0
|
|
|
0
|
1
|
0
|
my($self,@arg) = @_; |
|
627
|
|
|
|
|
|
|
|
|
628
|
0
|
|
|
|
|
0
|
my %hash; |
|
629
|
0
|
0
|
0
|
|
|
0
|
if (ref $arg[0] and ref $arg[0] eq 'HASH') { |
|
630
|
0
|
|
|
|
|
0
|
%hash = %{$arg[0]}; |
|
|
0
|
|
|
|
|
0
|
|
|
631
|
|
|
|
|
|
|
} else { |
|
632
|
0
|
|
|
|
|
0
|
%hash = @arg; |
|
633
|
|
|
|
|
|
|
} |
|
634
|
|
|
|
|
|
|
|
|
635
|
0
|
|
|
|
|
0
|
foreach (keys %hash) { |
|
636
|
0
|
|
|
|
|
0
|
$self->replace_list($_,$hash{$_}); |
|
637
|
|
|
|
|
|
|
} |
|
638
|
|
|
|
|
|
|
} |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
# Fetch the value at the specified key. In an array |
|
641
|
|
|
|
|
|
|
# context, this will return the entire array. In a scalar |
|
642
|
|
|
|
|
|
|
# context, this will return either the first or the last member |
|
643
|
|
|
|
|
|
|
# of the array, depending on the value of the global Fetchlast. |
|
644
|
|
|
|
|
|
|
# You can specify an optional index to index into the resultant |
|
645
|
|
|
|
|
|
|
# array. |
|
646
|
|
|
|
|
|
|
# Codes: |
|
647
|
|
|
|
|
|
|
# digit (12) returns the 12th item |
|
648
|
|
|
|
|
|
|
# hash sign (#) returns the last item |
|
649
|
|
|
|
|
|
|
# question mark (?) returns a random item |
|
650
|
|
|
|
|
|
|
# zero (0) returns the first item |
|
651
|
|
|
|
|
|
|
sub get { |
|
652
|
44
|
|
|
44
|
1
|
55
|
my($self,$key,$index) = @_; |
|
653
|
44
|
50
|
|
|
|
98
|
return $self->index($key) if $key=~/[.\[\]]/; |
|
654
|
|
|
|
|
|
|
|
|
655
|
44
|
50
|
|
|
|
70
|
if (defined $index) { |
|
656
|
0
|
0
|
0
|
|
|
0
|
return $self->get_last($key) if $index eq '#' || $index == -1; |
|
657
|
0
|
0
|
|
|
|
0
|
if ($index eq '?') { |
|
658
|
0
|
|
|
|
|
0
|
my $size = scalar(@{$self->{$key}}); |
|
|
0
|
|
|
|
|
0
|
|
|
659
|
0
|
|
|
|
|
0
|
return $self->{$key}->[rand($size)]; |
|
660
|
|
|
|
|
|
|
} |
|
661
|
0
|
0
|
|
|
|
0
|
return $self->{$key}->[$index] if $index ne ''; |
|
662
|
|
|
|
|
|
|
} |
|
663
|
|
|
|
|
|
|
|
|
664
|
44
|
100
|
|
|
|
75
|
if (wantarray) { |
|
665
|
37
|
50
|
|
|
|
69
|
return @{$self->{$key}} if $self->{$key}; |
|
|
37
|
|
|
|
|
107
|
|
|
666
|
0
|
|
|
|
|
0
|
return my(@empty); |
|
667
|
|
|
|
|
|
|
} |
|
668
|
7
|
50
|
|
|
|
22
|
return $self->get_first($key) unless $Fetchlast; |
|
669
|
0
|
|
|
|
|
0
|
return $self->get_last($key); |
|
670
|
|
|
|
|
|
|
} |
|
671
|
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
# Returns 1 if the key exists. |
|
673
|
|
|
|
|
|
|
sub exists { |
|
674
|
0
|
|
|
0
|
0
|
0
|
my($self,$key,$index) = @_; |
|
675
|
0
|
0
|
0
|
|
|
0
|
return 1 if defined($self->{$key}) && !$index; |
|
676
|
0
|
0
|
|
|
|
0
|
return 1 if defined($self->{$key}->[$index]); |
|
677
|
0
|
|
|
|
|
0
|
return undef; |
|
678
|
|
|
|
|
|
|
} |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
# return an array reference at indicated tag. |
|
681
|
|
|
|
|
|
|
# Equivalent to $stone->{'tag'} |
|
682
|
|
|
|
|
|
|
sub at { |
|
683
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
684
|
0
|
|
|
|
|
0
|
return $self->{$_[0]}; |
|
685
|
|
|
|
|
|
|
} |
|
686
|
|
|
|
|
|
|
# |
|
687
|
|
|
|
|
|
|
# Delete the indicated key entirely. |
|
688
|
|
|
|
|
|
|
sub delete { |
|
689
|
1
|
|
|
1
|
1
|
2
|
my($self,$key) = @_; |
|
690
|
1
|
|
|
|
|
4
|
delete $self->{$key}; |
|
691
|
1
|
|
|
|
|
2
|
$self->_fix_cursors; |
|
692
|
|
|
|
|
|
|
} |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
# Return all the tags in the stone. |
|
695
|
|
|
|
|
|
|
sub tags { |
|
696
|
15
|
|
|
15
|
1
|
15
|
my $self = shift; |
|
697
|
15
|
|
|
|
|
12
|
return grep (!/^\./,keys %{$self}); |
|
|
15
|
|
|
|
|
79
|
|
|
698
|
|
|
|
|
|
|
} |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
# Return attributes as a hash reference |
|
701
|
|
|
|
|
|
|
# (only used by asXML) |
|
702
|
|
|
|
|
|
|
sub attributes { |
|
703
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
704
|
0
|
|
|
|
|
0
|
my ($tag,$value) = @_; |
|
705
|
0
|
0
|
|
|
|
0
|
if (defined $tag) { |
|
706
|
0
|
0
|
|
|
|
0
|
return $self->{'.att'} = $tag if ref $tag eq 'HASH'; |
|
707
|
0
|
0
|
|
|
|
0
|
return $self->{'.att'}{$tag} = $value if defined $value; |
|
708
|
0
|
|
|
|
|
0
|
return $self->{'.att'}{$tag}; |
|
709
|
|
|
|
|
|
|
} |
|
710
|
0
|
|
0
|
|
|
0
|
return $self->{'.att'} ||= {}; |
|
711
|
|
|
|
|
|
|
} |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
# Fetch an Iterator on the Stone. |
|
715
|
|
|
|
|
|
|
sub cursor { |
|
716
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
717
|
0
|
|
|
|
|
0
|
return new Stone::Cursor($self); |
|
718
|
|
|
|
|
|
|
} |
|
719
|
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
# Convert a stone into a straight hash |
|
721
|
|
|
|
|
|
|
sub to_hash { |
|
722
|
0
|
|
|
0
|
0
|
0
|
my ($self) = shift; |
|
723
|
0
|
|
|
|
|
0
|
my ($key,%result); |
|
724
|
0
|
|
|
|
|
0
|
foreach $key (keys %$self) { |
|
725
|
0
|
0
|
|
|
|
0
|
next if substr($key,0,1) eq '.'; |
|
726
|
0
|
|
|
|
|
0
|
my ($value,@values); |
|
727
|
0
|
|
|
|
|
0
|
foreach $value (@{$self->{$key}}) { |
|
|
0
|
|
|
|
|
0
|
|
|
728
|
|
|
|
|
|
|
# NG 00-10-04 changed to convert values with .name into those names |
|
729
|
|
|
|
|
|
|
# NG 00-10-04 and to convert recursive results to HASH ref |
|
730
|
0
|
0
|
|
|
|
0
|
push(@values,!ref($value)? $value: |
|
|
|
0
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
defined ($value->{'.name'})? $value->{'.name'}: |
|
732
|
|
|
|
|
|
|
{$value->to_hash()}); |
|
733
|
|
|
|
|
|
|
} |
|
734
|
0
|
0
|
|
|
|
0
|
$result{$key} = @values > 1 ? [@values] : $values[0]; |
|
735
|
|
|
|
|
|
|
} |
|
736
|
0
|
|
|
|
|
0
|
return %result; |
|
737
|
|
|
|
|
|
|
} |
|
738
|
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
# Search for a particular tag and return it using a breadth-first search |
|
740
|
|
|
|
|
|
|
sub search { |
|
741
|
0
|
|
|
0
|
1
|
0
|
my ($self,$tag) = @_; |
|
742
|
0
|
0
|
|
|
|
0
|
return $self->get($tag) if $self->{$tag}; |
|
743
|
0
|
|
|
|
|
0
|
foreach ($self->tags()) { |
|
744
|
0
|
|
|
|
|
0
|
my @objects = $self->get($_); |
|
745
|
0
|
0
|
|
|
|
0
|
@objects = reverse(@objects) if $Fetchlast; |
|
746
|
0
|
|
|
|
|
0
|
foreach my $obj (@objects) { |
|
747
|
0
|
0
|
0
|
|
|
0
|
next unless ref($obj) and $obj->isa('Stone'); |
|
748
|
0
|
|
|
|
|
0
|
my @result = $obj->search($tag); |
|
749
|
0
|
0
|
|
|
|
0
|
return wantarray ? @result : ($Fetchlast ? $result[$#result] : $result[0]); |
|
|
|
0
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
} |
|
751
|
|
|
|
|
|
|
} |
|
752
|
0
|
0
|
|
|
|
0
|
return wantarray ? () : undef; |
|
753
|
|
|
|
|
|
|
} |
|
754
|
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
# Extended indexing, using a compound index that |
|
756
|
|
|
|
|
|
|
# looks like: |
|
757
|
|
|
|
|
|
|
# key1[index].key2[index].key3[index] |
|
758
|
|
|
|
|
|
|
# If indices are left out, then you can get |
|
759
|
|
|
|
|
|
|
# multiple values out: |
|
760
|
|
|
|
|
|
|
# 1. In a scalar context, you'll get the first or last |
|
761
|
|
|
|
|
|
|
# value from each position. |
|
762
|
|
|
|
|
|
|
# 2. In an array context, you'll get all the values! |
|
763
|
|
|
|
|
|
|
sub index { |
|
764
|
4
|
|
|
4
|
1
|
7
|
my($self,$index) = @_; |
|
765
|
4
|
|
|
|
|
14
|
return &_index($self,split(/\./,$index)); |
|
766
|
|
|
|
|
|
|
} |
|
767
|
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
sub _index { |
|
769
|
8
|
|
|
8
|
|
11
|
my($self,@indices) = @_; |
|
770
|
8
|
|
|
|
|
9
|
my(@value,$key,$position,$i); |
|
771
|
0
|
|
|
|
|
0
|
my(@results); |
|
772
|
8
|
|
|
|
|
9
|
$i = shift @indices; |
|
773
|
|
|
|
|
|
|
|
|
774
|
8
|
50
|
|
|
|
25
|
if (($key,$position) = $i=~/(.+)\[([\d\#\?]+)\]/) { # has a position |
|
|
|
100
|
|
|
|
|
|
|
775
|
0
|
|
|
|
|
0
|
@value = $self->get($key,$position); # always a scalar |
|
776
|
|
|
|
|
|
|
} elsif (wantarray) { |
|
777
|
6
|
|
|
|
|
11
|
@value = $self->get($i); |
|
778
|
|
|
|
|
|
|
} else { |
|
779
|
2
|
|
|
|
|
7
|
@value = scalar($self->get($i)); |
|
780
|
|
|
|
|
|
|
} |
|
781
|
|
|
|
|
|
|
|
|
782
|
8
|
|
|
|
|
12
|
foreach (@value) { |
|
783
|
10
|
50
|
|
|
|
19
|
next unless ref $_; |
|
784
|
10
|
100
|
|
|
|
15
|
if (@indices) { |
|
785
|
4
|
50
|
33
|
|
|
37
|
push @results,&_index($_,@indices) if $_->isa('Stone') && !exists($_->{'.name'}); |
|
786
|
|
|
|
|
|
|
} else{ |
|
787
|
6
|
|
|
|
|
12
|
push @results,$_; |
|
788
|
|
|
|
|
|
|
} |
|
789
|
|
|
|
|
|
|
} |
|
790
|
8
|
100
|
|
|
|
36
|
return wantarray ? @results : $results[0]; |
|
791
|
|
|
|
|
|
|
} |
|
792
|
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
# Return the data structure as a nicely-formatted tab-delimited table |
|
794
|
|
|
|
|
|
|
sub asTable { |
|
795
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
796
|
0
|
|
|
|
|
0
|
my $string = ''; |
|
797
|
0
|
|
|
|
|
0
|
$self->_asTable(\$string,0,0); |
|
798
|
0
|
|
|
|
|
0
|
return $string; |
|
799
|
|
|
|
|
|
|
} |
|
800
|
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
# Return the data structure as a nice string representation (problematic) |
|
802
|
|
|
|
|
|
|
sub asString { |
|
803
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
804
|
0
|
|
0
|
|
|
0
|
my $MAXWIDTH = shift || DEFAULT_WIDTH; |
|
805
|
0
|
|
|
|
|
0
|
my $tabs = $self->asTable; |
|
806
|
0
|
0
|
|
|
|
0
|
return '' unless $tabs; |
|
807
|
0
|
|
|
|
|
0
|
my(@lines) = split("\n",$tabs); |
|
808
|
0
|
|
|
|
|
0
|
my($result,@max); |
|
809
|
0
|
|
|
|
|
0
|
foreach (@lines) { |
|
810
|
0
|
|
|
|
|
0
|
my(@fields) = split("\t"); |
|
811
|
0
|
|
|
|
|
0
|
for (my $i=0;$i<@fields;$i++) { |
|
812
|
0
|
0
|
0
|
|
|
0
|
$max[$i] = length($fields[$i]) if |
|
813
|
|
|
|
|
|
|
!defined($max[$i]) or $max[$i] < length($fields[$i]); |
|
814
|
|
|
|
|
|
|
} |
|
815
|
|
|
|
|
|
|
} |
|
816
|
0
|
0
|
|
|
|
0
|
foreach (@max) { $_ = $MAXWIDTH if $_ > $MAXWIDTH; } # crunch long lines |
|
|
0
|
|
|
|
|
0
|
|
|
817
|
0
|
|
|
|
|
0
|
my $format1 = join(' ',map { "^"."<"x $max[$_] } (0..$#max)) . "\n"; |
|
|
0
|
|
|
|
|
0
|
|
|
818
|
0
|
|
|
|
|
0
|
my $format2 = ' ' . join(' ',map { "^"."<"x ($max[$_]-1) } (0..$#max)) . "~~\n"; |
|
|
0
|
|
|
|
|
0
|
|
|
819
|
0
|
|
|
|
|
0
|
$^A = ''; |
|
820
|
0
|
|
|
|
|
0
|
foreach (@lines) { |
|
821
|
0
|
|
|
|
|
0
|
my @data = split("\t"); |
|
822
|
0
|
|
|
|
|
0
|
push(@data,('')x(@max-@data)); |
|
823
|
0
|
|
|
|
|
0
|
formline ($format1,@data); |
|
824
|
0
|
|
|
|
|
0
|
formline ($format2,@data); |
|
825
|
|
|
|
|
|
|
} |
|
826
|
0
|
|
|
|
|
0
|
return ($result = $^A,$^A='')[0]; |
|
827
|
|
|
|
|
|
|
} |
|
828
|
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
# Return the data structure as an HTML table |
|
830
|
|
|
|
|
|
|
sub asHTML { |
|
831
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
832
|
0
|
|
|
|
|
0
|
my $modify = shift; |
|
833
|
0
|
|
0
|
|
|
0
|
$modify ||= \&_default_modify_html; |
|
834
|
0
|
|
|
|
|
0
|
my $string = "\n";
|
835
|
0
|
|
|
|
|
0
|
$self->_asHTML(\$string,$modify,0,0); |
|
836
|
0
|
|
|
|
|
0
|
$string .= " | \n "; |
|
837
|
0
|
|
|
|
|
0
|
return $string; |
|
838
|
|
|
|
|
|
|
} |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
# Return data structure using XML syntax |
|
841
|
|
|
|
|
|
|
# Top-level tag is unless otherwise specified |
|
842
|
|
|
|
|
|
|
sub asXML { |
|
843
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
844
|
0
|
|
0
|
|
|
0
|
my $top = shift || "Stone"; |
|
845
|
0
|
|
0
|
|
|
0
|
my $modify = shift || \&_default_modify_xml; |
|
846
|
0
|
|
|
|
|
0
|
my $att; |
|
847
|
0
|
0
|
|
|
|
0
|
if (exists($self->{'.att'})) { |
|
848
|
0
|
|
|
|
|
0
|
my $a = $self->attributes; |
|
849
|
0
|
|
|
|
|
0
|
foreach (keys %$a) { |
|
850
|
0
|
|
|
|
|
0
|
$att .= qq( $_="$a->{$_}"); |
|
851
|
|
|
|
|
|
|
} |
|
852
|
|
|
|
|
|
|
} |
|
853
|
0
|
|
|
|
|
0
|
my $string = "<${top}${att}>\n"; |
|
854
|
0
|
|
|
|
|
0
|
$self->_asXML(\$string,$modify,0,1); |
|
855
|
0
|
|
|
|
|
0
|
$string .="$top>\n"; |
|
856
|
0
|
|
|
|
|
0
|
return $string; |
|
857
|
|
|
|
|
|
|
} |
|
858
|
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
# This is the method used for string interpolation |
|
860
|
|
|
|
|
|
|
sub toString { |
|
861
|
44
|
|
|
44
|
1
|
89
|
my $self = shift; |
|
862
|
44
|
100
|
|
|
|
192
|
return $self->{'.name'} if exists $self->{'.name'}; |
|
863
|
6
|
|
|
|
|
13
|
my @tags = map { my @v = $self->get($_); |
|
|
16
|
|
|
|
|
29
|
|
|
864
|
16
|
|
|
|
|
19
|
my $cnt = scalar @v; |
|
865
|
16
|
|
|
|
|
38
|
"$_($cnt)" |
|
866
|
|
|
|
|
|
|
} $self->tags; |
|
867
|
6
|
100
|
|
|
|
17
|
return '' unless @tags; |
|
868
|
5
|
|
|
|
|
23
|
return join ',',@tags; |
|
869
|
|
|
|
|
|
|
} |
|
870
|
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
sub _asTable { |
|
873
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
874
|
0
|
|
|
|
|
0
|
my ($string,$position,$level) = @_; |
|
875
|
0
|
|
|
|
|
0
|
my $pos = $position; |
|
876
|
0
|
|
|
|
|
0
|
foreach my $tag ($self->tags) { |
|
877
|
0
|
|
|
|
|
0
|
my @values = $self->get($tag); |
|
878
|
0
|
|
|
|
|
0
|
foreach my $value (@values) { |
|
879
|
0
|
|
|
|
|
0
|
$$string .= "\t" x ($level-$pos) . "$tag\t"; |
|
880
|
0
|
|
|
|
|
0
|
$pos = $level+1; |
|
881
|
0
|
0
|
|
|
|
0
|
if (exists $value->{'.name'}) { |
|
882
|
0
|
|
|
|
|
0
|
$$string .= "\t" x ($level-$pos+1) . "$value\n"; |
|
883
|
0
|
|
|
|
|
0
|
$pos=0; |
|
884
|
|
|
|
|
|
|
} else { |
|
885
|
0
|
|
|
|
|
0
|
$pos = $value->_asTable($string,$pos,$level+1); |
|
886
|
|
|
|
|
|
|
} |
|
887
|
|
|
|
|
|
|
} |
|
888
|
|
|
|
|
|
|
} |
|
889
|
0
|
|
|
|
|
0
|
return $pos; |
|
890
|
|
|
|
|
|
|
} |
|
891
|
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
sub _asXML { |
|
893
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
894
|
0
|
|
|
|
|
0
|
my ($string,$modify,$pos,$level) = @_; |
|
895
|
0
|
|
|
|
|
0
|
foreach my $tag ($self->tags) { |
|
896
|
0
|
|
|
|
|
0
|
my @values = $self->get($tag); |
|
897
|
0
|
|
|
|
|
0
|
foreach my $value (@values) { |
|
898
|
0
|
0
|
|
|
|
0
|
my($title,$contents) = $modify ? $modify->($tag,$value) : ($tag,$value); |
|
899
|
0
|
|
|
|
|
0
|
my $att; |
|
900
|
|
|
|
|
|
|
|
|
901
|
0
|
0
|
|
|
|
0
|
if (exists $value->{'.att'}) { |
|
902
|
0
|
|
|
|
|
0
|
my $a = $value->{'.att'}; |
|
903
|
0
|
|
|
|
|
0
|
foreach (keys %$a) { |
|
904
|
0
|
|
|
|
|
0
|
$att .= qq( $_="$a->{$_}"); |
|
905
|
|
|
|
|
|
|
} |
|
906
|
|
|
|
|
|
|
} |
|
907
|
|
|
|
|
|
|
|
|
908
|
0
|
|
|
|
|
0
|
$$string .= ' ' x ($level-$pos) . "<${title}${att}>"; |
|
909
|
0
|
|
|
|
|
0
|
$pos = $level+1; |
|
910
|
|
|
|
|
|
|
|
|
911
|
0
|
0
|
|
|
|
0
|
if (exists $value->{'.name'}) { |
|
912
|
0
|
|
|
|
|
0
|
$$string .= ' ' x ($level-$pos+1) . "$contents$title>\n"; |
|
913
|
0
|
|
|
|
|
0
|
$pos=0; |
|
914
|
|
|
|
|
|
|
} else { |
|
915
|
0
|
|
|
|
|
0
|
$$string .= "\n" . ' ' x ($level+1); |
|
916
|
0
|
|
|
|
|
0
|
$pos = $value->_asXML($string,$modify,$pos,$level+1); |
|
917
|
0
|
|
|
|
|
0
|
$$string .= ' ' x ($level-$pos) . "$title>\n"; |
|
918
|
|
|
|
|
|
|
} |
|
919
|
|
|
|
|
|
|
} |
|
920
|
|
|
|
|
|
|
} |
|
921
|
0
|
|
|
|
|
0
|
return $pos; |
|
922
|
|
|
|
|
|
|
} |
|
923
|
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
sub _asHTML { |
|
925
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
926
|
0
|
|
|
|
|
0
|
my ($string,$modify,$position,$level) = @_; |
|
927
|
0
|
|
|
|
|
0
|
my $pos = $position; |
|
928
|
0
|
|
|
|
|
0
|
foreach my $tag ($self->tags) { |
|
929
|
0
|
|
|
|
|
0
|
my @values = $self->get($tag); |
|
930
|
0
|
|
|
|
|
0
|
foreach my $value (@values) { |
|
931
|
0
|
|
|
|
|
0
|
my($title,$contents) = $modify->($tag,$value); |
|
932
|
0
|
0
|
|
|
|
0
|
$$string .= " |
" unless $position;
|
933
|
0
|
|
|
|
|
0
|
$$string .= " | | " x ($level-$pos) . "$title | ";
|
934
|
0
|
|
|
|
|
0
|
$pos = $level+1; |
|
935
|
0
|
0
|
|
|
|
0
|
if (exists $value->{'.name'}) { |
|
936
|
0
|
|
|
|
|
0
|
$$string .= " | | " x ($level-$pos+1) . "$contents |
\n";
|
937
|
0
|
|
|
|
|
0
|
$pos=0; |
|
938
|
|
|
|
|
|
|
} else { |
|
939
|
0
|
|
|
|
|
0
|
$pos = $value->_asHTML($string,$modify,$pos,$level+1); |
|
940
|
|
|
|
|
|
|
} |
|
941
|
|
|
|
|
|
|
} |
|
942
|
|
|
|
|
|
|
} |
|
943
|
|
|
|
|
|
|
|
|
944
|
0
|
|
|
|
|
0
|
return $pos; |
|
945
|
|
|
|
|
|
|
} |
|
946
|
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
sub _default_modify_html { |
|
948
|
0
|
|
|
0
|
|
0
|
my ($tag,$value) = @_; |
|
949
|
0
|
|
|
|
|
0
|
return ("$tag",$value); |
|
950
|
|
|
|
|
|
|
} |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
sub _default_modify_xml { |
|
953
|
0
|
|
|
0
|
|
0
|
my ($tag,$value) = @_; |
|
954
|
0
|
|
|
|
|
0
|
$value =~ s/&/&/g; |
|
955
|
0
|
|
|
|
|
0
|
$value =~ s/>/>/g; |
|
956
|
0
|
|
|
|
|
0
|
$value =~ s/</g; |
|
957
|
0
|
|
|
|
|
0
|
($tag,$value); |
|
958
|
|
|
|
|
|
|
} |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
# Dump the entire data structure, for debugging purposes |
|
961
|
|
|
|
|
|
|
sub dump { |
|
962
|
0
|
|
|
0
|
1
|
0
|
my($self) = shift; |
|
963
|
0
|
|
|
|
|
0
|
my $i = $self->cursor; |
|
964
|
0
|
|
|
|
|
0
|
my ($key,$value); |
|
965
|
0
|
|
|
|
|
0
|
while (($key,$value)=$i->each) { |
|
966
|
0
|
|
|
|
|
0
|
print "$key=$value\n"; |
|
967
|
|
|
|
|
|
|
} |
|
968
|
|
|
|
|
|
|
# this has to be done explicitly here or it won't happen. |
|
969
|
0
|
|
|
|
|
0
|
$i->DESTROY; |
|
970
|
|
|
|
|
|
|
} |
|
971
|
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
# return the name of the Stone |
|
973
|
|
|
|
|
|
|
sub name { |
|
974
|
0
|
0
|
|
0
|
0
|
0
|
$_[0]->{'.name'} = $_[1] if defined $_[1]; |
|
975
|
0
|
|
|
|
|
0
|
return $_[0]->{'.name'} |
|
976
|
|
|
|
|
|
|
} |
|
977
|
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
# --------- LOW LEVEL DATA INSERTION ROUTINES --------- |
|
980
|
|
|
|
|
|
|
# Append a set of values to the key. |
|
981
|
|
|
|
|
|
|
# One or more values may be other Stones. |
|
982
|
|
|
|
|
|
|
# You can pass the same value multiple times |
|
983
|
|
|
|
|
|
|
# to enter multiple values, or alternatively |
|
984
|
|
|
|
|
|
|
# pass an anonymous array. |
|
985
|
|
|
|
|
|
|
sub insert_list { |
|
986
|
33
|
|
|
33
|
1
|
52
|
my($self,$key,@values) = @_; |
|
987
|
|
|
|
|
|
|
|
|
988
|
33
|
|
|
|
|
40
|
foreach (@values) { |
|
989
|
33
|
|
|
|
|
36
|
my $ref = ref($_); |
|
990
|
|
|
|
|
|
|
|
|
991
|
33
|
100
|
|
|
|
56
|
if (!$ref) { # Inserting a scalar |
|
992
|
27
|
|
|
|
|
61
|
my $s = new Stone; |
|
993
|
27
|
|
|
|
|
52
|
$s->{'.name'} = $_; |
|
994
|
27
|
|
|
|
|
24
|
push(@{$self->{$key}},$s); |
|
|
27
|
|
|
|
|
54
|
|
|
995
|
27
|
|
|
|
|
53
|
next; |
|
996
|
|
|
|
|
|
|
} |
|
997
|
|
|
|
|
|
|
|
|
998
|
6
|
100
|
|
|
|
14
|
if ($ref=~/Stone/) { # A simple insertion |
|
999
|
3
|
|
|
|
|
3
|
push(@{$self->{$key}},$_); |
|
|
3
|
|
|
|
|
5
|
|
|
1000
|
3
|
|
|
|
|
7
|
next; |
|
1001
|
|
|
|
|
|
|
} |
|
1002
|
|
|
|
|
|
|
|
|
1003
|
3
|
50
|
|
|
|
14
|
if ($ref eq 'ARRAY') { # A multivalued insertion |
|
1004
|
0
|
|
|
|
|
0
|
$self->insert_list($key,@{$_}); # Recursive insertion |
|
|
0
|
|
|
|
|
0
|
|
|
1005
|
0
|
|
|
|
|
0
|
next; |
|
1006
|
|
|
|
|
|
|
} |
|
1007
|
|
|
|
|
|
|
|
|
1008
|
3
|
50
|
|
|
|
5
|
if ($ref eq 'HASH') { # Insert a record, potentially recursively |
|
1009
|
3
|
|
|
|
|
4
|
$self->insert_hash($key,%{$_}); |
|
|
3
|
|
|
|
|
13
|
|
|
1010
|
3
|
|
|
|
|
6
|
next; |
|
1011
|
|
|
|
|
|
|
} |
|
1012
|
|
|
|
|
|
|
|
|
1013
|
0
|
|
|
|
|
0
|
warn "Attempting to insert a $ref into a Stone. Be alert.\n"; |
|
1014
|
0
|
|
|
|
|
0
|
push(@{$self->{$key}},$_); |
|
|
0
|
|
|
|
|
0
|
|
|
1015
|
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
33
|
|
|
|
|
61
|
$self->_fix_cursors; |
|
1018
|
|
|
|
|
|
|
} |
|
1019
|
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
# Put the values into the key, replacing |
|
1021
|
|
|
|
|
|
|
# whatever was there before. |
|
1022
|
|
|
|
|
|
|
sub replace_list { |
|
1023
|
0
|
|
|
0
|
1
|
0
|
my($self,$key,@values) = @_; |
|
1024
|
0
|
|
|
|
|
0
|
$self->{$key}=[]; # clear it out |
|
1025
|
0
|
|
|
|
|
0
|
$self->insert_list($key,@values); # append the values |
|
1026
|
|
|
|
|
|
|
} |
|
1027
|
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
# Similar to put_record, but doesn't overwrite the |
|
1029
|
|
|
|
|
|
|
# previous value of the key. |
|
1030
|
|
|
|
|
|
|
sub insert_hash { |
|
1031
|
3
|
|
|
3
|
1
|
562
|
my($self,$key,%values) = @_; |
|
1032
|
3
|
|
|
|
|
8
|
my($newrecord) = $self->new_record($key); |
|
1033
|
3
|
|
|
|
|
7
|
foreach (keys %values) { |
|
1034
|
8
|
|
|
|
|
18
|
$newrecord->insert_list($_,$values{$_}); |
|
1035
|
|
|
|
|
|
|
} |
|
1036
|
|
|
|
|
|
|
} |
|
1037
|
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
# Put a new associative array at the indicated key, |
|
1039
|
|
|
|
|
|
|
# replacing whatever was there before. Multiple values |
|
1040
|
|
|
|
|
|
|
# can be represented with an anonymous ARRAY reference. |
|
1041
|
|
|
|
|
|
|
sub replace_hash { |
|
1042
|
0
|
|
|
0
|
1
|
0
|
my($self,$key,%values) = @_; |
|
1043
|
0
|
|
|
|
|
0
|
$self->{$key}=[]; # clear it out |
|
1044
|
0
|
|
|
|
|
0
|
$self->insert_hash($key,%values); |
|
1045
|
|
|
|
|
|
|
} |
|
1046
|
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
#------------------- PRIVATE SUBROUTINES----------- |
|
1048
|
|
|
|
|
|
|
# Create a new record at indicated key |
|
1049
|
|
|
|
|
|
|
# and return it. |
|
1050
|
|
|
|
|
|
|
sub new_record { |
|
1051
|
3
|
|
|
3
|
0
|
4
|
my($self,$key) = @_; |
|
1052
|
3
|
|
|
|
|
4
|
my $stone = new Stone(); |
|
1053
|
3
|
|
|
|
|
4
|
push(@{$self->{$key}},$stone); |
|
|
3
|
|
|
|
|
6
|
|
|
1054
|
3
|
|
|
|
|
4
|
return $stone; |
|
1055
|
|
|
|
|
|
|
} |
|
1056
|
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
sub get_first { |
|
1058
|
7
|
|
|
7
|
0
|
8
|
my($self,$key) = @_; |
|
1059
|
7
|
|
|
|
|
27
|
return $self->{$key}->[0]; |
|
1060
|
|
|
|
|
|
|
} |
|
1061
|
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
sub get_last { |
|
1063
|
0
|
|
|
0
|
0
|
0
|
my($self,$key) = @_; |
|
1064
|
0
|
|
|
|
|
0
|
return $self->{$key}->[$#{$self->{$key}}]; |
|
|
0
|
|
|
|
|
0
|
|
|
1065
|
|
|
|
|
|
|
} |
|
1066
|
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
# This is a private subroutine used for registering |
|
1068
|
|
|
|
|
|
|
# and unregistering cursors |
|
1069
|
|
|
|
|
|
|
sub _register_cursor { |
|
1070
|
0
|
|
|
0
|
|
0
|
my($self,$cursor,$register) = @_; |
|
1071
|
0
|
0
|
|
|
|
0
|
if ($register) { |
|
1072
|
0
|
|
|
|
|
0
|
$self->{'.cursors'}->{$cursor}=$cursor; |
|
1073
|
|
|
|
|
|
|
} else { |
|
1074
|
0
|
|
|
|
|
0
|
delete $self->{'.cursors'}->{$cursor}; |
|
1075
|
0
|
0
|
|
|
|
0
|
delete $self->{'.cursors'} unless %{$self->{'.cursors'}}; |
|
|
0
|
|
|
|
|
0
|
|
|
1076
|
|
|
|
|
|
|
} |
|
1077
|
|
|
|
|
|
|
} |
|
1078
|
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
# This is a private subroutine used to alert cursors that |
|
1080
|
|
|
|
|
|
|
# our contents have changed. |
|
1081
|
|
|
|
|
|
|
sub _fix_cursors { |
|
1082
|
34
|
|
|
34
|
|
35
|
my($self) = @_; |
|
1083
|
34
|
50
|
|
|
|
150
|
return unless $self->{'.cursors'}; |
|
1084
|
0
|
|
|
|
|
0
|
my($cursor); |
|
1085
|
0
|
|
|
|
|
0
|
foreach $cursor (values %{$self->{'.cursors'}}) { |
|
|
0
|
|
|
|
|
0
|
|
|
1086
|
0
|
|
|
|
|
0
|
$cursor->reset; |
|
1087
|
|
|
|
|
|
|
} |
|
1088
|
|
|
|
|
|
|
} |
|
1089
|
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
# This is a private subroutine. It indexes |
|
1091
|
|
|
|
|
|
|
# all the way into the structure. |
|
1092
|
|
|
|
|
|
|
#sub _index { |
|
1093
|
|
|
|
|
|
|
# my($self,@indices) = @_; |
|
1094
|
|
|
|
|
|
|
# my $stone = $self; |
|
1095
|
|
|
|
|
|
|
# my($key,$index,@h); |
|
1096
|
|
|
|
|
|
|
# while (($key,$index) = splice(@indices,0,2)) { |
|
1097
|
|
|
|
|
|
|
# unless (defined($index)) { |
|
1098
|
|
|
|
|
|
|
# return scalar($stone->get($key)) unless wantarray; |
|
1099
|
|
|
|
|
|
|
# return @h = $stone->get($key) if wantarray; |
|
1100
|
|
|
|
|
|
|
# } else { |
|
1101
|
|
|
|
|
|
|
# $stone= ($index eq "\#") ? $stone->get_last($key): |
|
1102
|
|
|
|
|
|
|
# $stone->get($key,$index); |
|
1103
|
|
|
|
|
|
|
# last unless ref($stone)=~/Stone/; |
|
1104
|
|
|
|
|
|
|
# } |
|
1105
|
|
|
|
|
|
|
# } |
|
1106
|
|
|
|
|
|
|
# return $stone; |
|
1107
|
|
|
|
|
|
|
#} |
|
1108
|
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
sub DESTROY { |
|
1110
|
38
|
|
|
38
|
|
51
|
my $self = shift; |
|
1111
|
38
|
|
|
|
|
35
|
undef %{$self->{'.cursor'}}; # not really necessary ? |
|
|
38
|
|
|
|
|
243
|
|
|
1112
|
|
|
|
|
|
|
} |
|
1113
|
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
1; |