line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# |
2
|
|
|
|
|
|
|
# This file is part of TBX-XCS |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# This software is copyright (c) 2013 by Alan K. Melby. |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# This is free software; you can redistribute it and/or modify it under |
7
|
|
|
|
|
|
|
# the same terms as the Perl 5 programming language system itself. |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
package TBX::XCS::JSON; |
10
|
2
|
|
|
2
|
|
90611
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
71
|
|
11
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
52
|
|
12
|
2
|
|
|
2
|
|
1125
|
use TBX::XCS; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use JSON; |
14
|
|
|
|
|
|
|
use Carp; |
15
|
|
|
|
|
|
|
#carp from calling package, not from here |
16
|
|
|
|
|
|
|
our @CARP_NOT = qw(TBX::XCS::JSON); |
17
|
|
|
|
|
|
|
use Exporter::Easy ( |
18
|
|
|
|
|
|
|
OK => [qw(xcs_from_json json_from_xcs)], |
19
|
|
|
|
|
|
|
); |
20
|
|
|
|
|
|
|
our $VERSION = '0.05'; # VERSION |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# ABSTRACT: Read and write XCS data in JSON |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
#default: read XCS file and dump JSON data to STDOUT |
26
|
|
|
|
|
|
|
print json_from_xcs(TBX::XCS->new(file => $ARGV[0])) |
27
|
|
|
|
|
|
|
unless caller; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub json_from_xcs { |
31
|
|
|
|
|
|
|
my ($xcs) = @_; |
32
|
|
|
|
|
|
|
return to_json($xcs->{data}, {utf8 => 1, pretty => 1}); |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub xcs_from_json { |
37
|
|
|
|
|
|
|
my ($json) = @_; |
38
|
|
|
|
|
|
|
my $struct = decode_json $json; |
39
|
|
|
|
|
|
|
_check_structure($struct); |
40
|
|
|
|
|
|
|
my $xcs = {}; |
41
|
|
|
|
|
|
|
$xcs->{data} = $struct; |
42
|
|
|
|
|
|
|
return bless $xcs, 'TBX::XCS'; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub _check_structure { |
46
|
|
|
|
|
|
|
my ($struct) = @_; |
47
|
|
|
|
|
|
|
if(exists $struct->{constraints}){ |
48
|
|
|
|
|
|
|
_check_languages($struct->{constraints}); |
49
|
|
|
|
|
|
|
_check_refObjects($struct->{constraints}); |
50
|
|
|
|
|
|
|
_check_datCatSet($struct->{constraints}); |
51
|
|
|
|
|
|
|
}else{ |
52
|
|
|
|
|
|
|
croak 'no constraints key specified'; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
if(ref $struct->{name}){ |
55
|
|
|
|
|
|
|
croak 'name value should be a plain string'; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
if(ref $struct->{title}){ |
58
|
|
|
|
|
|
|
croak 'title value should be a plain string'; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
return; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub _check_languages { |
64
|
|
|
|
|
|
|
my ($constraints) = @_; |
65
|
|
|
|
|
|
|
if(exists $constraints->{languages}){ |
66
|
|
|
|
|
|
|
ref $constraints->{languages} eq 'HASH' |
67
|
|
|
|
|
|
|
or croak '"languages" value should be a hash of ' . |
68
|
|
|
|
|
|
|
'language abbreviations and names'; |
69
|
|
|
|
|
|
|
}else{ |
70
|
|
|
|
|
|
|
croak 'no "languages" key in constraints value'; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
return; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub _check_refObjects { |
76
|
|
|
|
|
|
|
my ($constraints) = @_; |
77
|
|
|
|
|
|
|
#if they don't exist, fine; we don't check them anyway |
78
|
|
|
|
|
|
|
exists $constraints->{refObjects} or return; |
79
|
|
|
|
|
|
|
my $refObjects = $constraints->{refObjects}; |
80
|
|
|
|
|
|
|
if('HASH' ne ref $refObjects){ |
81
|
|
|
|
|
|
|
croak "refObjects should be a hash"; |
82
|
|
|
|
|
|
|
}; |
83
|
|
|
|
|
|
|
#empty means none allowed |
84
|
|
|
|
|
|
|
if(!keys %$refObjects){ |
85
|
|
|
|
|
|
|
return; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
for (keys %$refObjects) { |
88
|
|
|
|
|
|
|
croak "Reference object $_ is not an array" |
89
|
|
|
|
|
|
|
unless 'ARRAY' eq ref $refObjects->{$_}; |
90
|
|
|
|
|
|
|
for my $element (@{ $refObjects->{$_} }){ |
91
|
|
|
|
|
|
|
croak "Reference object $_ should refer to an array of strings" |
92
|
|
|
|
|
|
|
if(ref $element); |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
return; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub _check_datCatSet { |
99
|
|
|
|
|
|
|
my ($constraints) = @_; |
100
|
|
|
|
|
|
|
if(!exists $constraints->{datCatSet}){ |
101
|
|
|
|
|
|
|
croak '"constraints" is missing key "datCatSet"'; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
my $datCatSet = $constraints->{datCatSet}; |
104
|
|
|
|
|
|
|
if(!keys %$datCatSet){ |
105
|
|
|
|
|
|
|
croak 'datCatSet should not be empty'; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
for my $meta_cat (keys %$datCatSet){ |
108
|
|
|
|
|
|
|
my $data_cats = $datCatSet->{$meta_cat}; |
109
|
|
|
|
|
|
|
_check_meta_cat($meta_cat, $data_cats); |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
return; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub _check_meta_cat { |
115
|
|
|
|
|
|
|
my ($meta_cat, $data_cats) = @_; |
116
|
|
|
|
|
|
|
TBX::XCS::_check_meta_cat($meta_cat); |
117
|
|
|
|
|
|
|
if(ref $data_cats ne 'ARRAY'){ |
118
|
|
|
|
|
|
|
croak "meta data category '$meta_cat' should be an array"; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
for my $data_cat (@$data_cats){ |
121
|
|
|
|
|
|
|
_check_data_category($meta_cat, $data_cat); |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
return; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub _check_data_category { |
127
|
|
|
|
|
|
|
my ($meta_cat, $data_cat) = @_; |
128
|
|
|
|
|
|
|
if( ref $data_cat ne 'HASH'){ |
129
|
|
|
|
|
|
|
croak "data category for $meta_cat should be a hash"; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
if(!exists $data_cat->{name}){ |
132
|
|
|
|
|
|
|
croak "missing name in data category of $meta_cat"; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
_check_datatype($meta_cat, $data_cat); |
135
|
|
|
|
|
|
|
if($meta_cat eq 'descrip'){ |
136
|
|
|
|
|
|
|
if(! exists $data_cat->{levels}){ |
137
|
|
|
|
|
|
|
croak "missing levels for $data_cat->{name}"; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
for my $level (@{ $data_cat->{levels} }){ |
140
|
|
|
|
|
|
|
croak "levels in $data_cat->{name} should be single values" |
141
|
|
|
|
|
|
|
if ref $level; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
TBX::XCS::_check_levels($data_cat); |
144
|
|
|
|
|
|
|
for my $level (@{ $data_cat->{levels} }){ |
145
|
|
|
|
|
|
|
croak "levels in $data_cat->{name} should be single values" |
146
|
|
|
|
|
|
|
if ref $level; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
if(exists $data_cat->{targetType}){ |
150
|
|
|
|
|
|
|
croak "targetType of $data_cat->{name} should be a string" |
151
|
|
|
|
|
|
|
if(ref $data_cat->{targetType}); |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
if(exists $data_cat->{forTermComp}){ |
154
|
|
|
|
|
|
|
if(JSON::is_bool($data_cat->{forTermComp})){ |
155
|
|
|
|
|
|
|
if($data_cat->{forTermComp}){ |
156
|
|
|
|
|
|
|
$data_cat->{forTermComp} = "yes"; |
157
|
|
|
|
|
|
|
}else{ |
158
|
|
|
|
|
|
|
$data_cat->{forTermComp} = "no"; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
if(ref $data_cat->{forTermComp}){ |
162
|
|
|
|
|
|
|
croak "forTermComp isn't a single value in $data_cat->{name}"; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
return; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub _check_datatype { |
169
|
|
|
|
|
|
|
my ($meta_cat, $data_cat) = @_; |
170
|
|
|
|
|
|
|
my $datatype = $data_cat->{datatype}; |
171
|
|
|
|
|
|
|
if($meta_cat eq 'termCompList'){ |
172
|
|
|
|
|
|
|
croak "termCompList cannot contain datatype" |
173
|
|
|
|
|
|
|
if $datatype; |
174
|
|
|
|
|
|
|
}else{ |
175
|
|
|
|
|
|
|
if(!$datatype){ |
176
|
|
|
|
|
|
|
$data_cat->{datatype} = TBX::XCS::_get_default_datatype($meta_cat); |
177
|
|
|
|
|
|
|
}else{ |
178
|
|
|
|
|
|
|
TBX::XCS::_check_datatype($meta_cat, $datatype); |
179
|
|
|
|
|
|
|
_check_picklist($data_cat) |
180
|
|
|
|
|
|
|
if($datatype eq 'picklist'); |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
return; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub _check_picklist { |
187
|
|
|
|
|
|
|
my ($data_cat) = @_; |
188
|
|
|
|
|
|
|
if(! exists $data_cat->{choices}){ |
189
|
|
|
|
|
|
|
croak "need choices for picklist in $data_cat->{name}"; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
my $choices = $data_cat->{choices}; |
192
|
|
|
|
|
|
|
if(ref $choices ne 'ARRAY'){ |
193
|
|
|
|
|
|
|
croak "$data_cat->{name} choices should be an array" |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
for(@$choices){ |
196
|
|
|
|
|
|
|
croak "$data_cat->{name} choices array elements should be strings" |
197
|
|
|
|
|
|
|
if(ref $_); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
return; |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
1; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
__END__ |