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