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