File Coverage

blib/lib/TBX/XCS/JSON.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


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__