1 // Copyright (c) Lawrence Livermore National Security, LLC and other Conduit
2 // Project developers. See top-level LICENSE AND COPYRIGHT files for dates and
3 // other details. No copyright assignment is required to contribute to Conduit.
4 
5 //-----------------------------------------------------------------------------
6 ///
7 /// file: conduit_blueprint_table.cpp
8 ///
9 //-----------------------------------------------------------------------------
10 
11 //-----------------------------------------------------------------------------
12 // std lib includes
13 //-----------------------------------------------------------------------------
14 // std lib includes will go here
15 
16 //-----------------------------------------------------------------------------
17 // conduit includes
18 //-----------------------------------------------------------------------------
19 #include "conduit_blueprint_table.hpp"
20 #include "conduit_log.hpp"
21 #include "conduit_blueprint_mcarray.hpp"
22 
23 using namespace conduit;
24 
25 //-----------------------------------------------------------------------------
26 // -- begin internal helper functions --
27 //-----------------------------------------------------------------------------
28 static const std::string PROTOCOL = "table";
29 
30 //-----------------------------------------------------------------------------
31 static bool
verify_table_values(const Node & n,Node & info)32 verify_table_values(const Node &n, Node &info)
33 {
34     bool res = true;
35 
36     // Each entry in n represents a column in the table.
37     // All columns must have the same length.
38     auto columns = n.children();
39     index_t num_rows = 0;
40     index_t num_cols = 0;
41     bool first_column = true;
42     while(columns.has_next())
43     {
44         const Node &n_col = columns.next();
45         Node &i_col = info[n_col.name()];
46         index_t this_num_elems = 0;
47         bool this_res = true;
48 
49         // Each entry must be a data array or a valid mlarray
50         if(n_col.dtype().is_list() || n_col.dtype().is_object())
51         {
52             this_res = blueprint::mcarray::verify(n_col, i_col);
53             this_num_elems = (res) ? n_col[0].dtype().number_of_elements() : 0;
54         }
55         else if(n_col.dtype().is_empty())
56         {
57             this_res = false;
58         }
59         else
60         {
61             this_num_elems = n_col.dtype().number_of_elements();
62         }
63 
64         // Record the error if we have one
65         if(!this_res)
66         {
67             utils::log::error(info, PROTOCOL, "child " + utils::log::quote(n_col.name()) + " is not a data_array or valid mcarray.");
68         }
69         else
70         {
71             // Check that number of elements matches for each column
72             // If this is the first column, set num_rows first.
73             num_rows = (first_column) ? this_num_elems : num_rows;
74             i_col["elements"] = this_num_elems;
75             if(num_rows != this_num_elems)
76             {
77                 utils::log::error(info, PROTOCOL, "child " + utils::log::quote(n_col.name()) + " does not contain the correct number of elements.");
78                 this_res = false;
79             }
80         }
81 
82         utils::log::validation(i_col, this_res);
83         res &= this_res;
84         first_column = false;
85         num_cols++;
86     }
87 
88     // Add some extra info for a valid table.
89     if(res)
90     {
91         info["columns"] = num_cols;
92         info["rows"] = num_rows;
93     }
94     utils::log::validation(info, res);
95     return res;
96 }
97 
98 //-----------------------------------------------------------------------------
99 static bool
verify_single_table(const Node & n,Node & info)100 verify_single_table(const Node &n, Node &info)
101 {
102     bool res = true;
103 
104     // "values" child must exist
105     if(!n.has_child("values"))
106     {
107         res = false;
108         utils::log::error(info, PROTOCOL, "missing child" + utils::log::quote("values", 1));
109     }
110 
111     if(res)
112     {
113         // "values" child must be a list or an object
114         const Node &n_values = n["values"];
115         Node &i_values = info["values"];
116         if(!n_values.dtype().is_object() && !n_values.dtype().is_list())
117         {
118             res = false;
119             utils::log::error(i_values, PROTOCOL, utils::log::quote("values", 0) + " must be an object or a list.");
120         }
121 
122         if(res)
123         {
124             res = verify_table_values(n_values, i_values);
125         }
126     }
127 
128     utils::log::validation(info, res);
129     return res;
130 }
131 
132 //-----------------------------------------------------------------------------
133 static bool
verify_many_tables(const conduit::Node & n,conduit::Node & info)134 verify_many_tables(const conduit::Node &n, conduit::Node &info)
135 {
136     bool res = true;
137 
138     // Check if each child is a table
139     auto children = n.children();
140     index_t num_tables = 0;
141     while(children.has_next())
142     {
143         const Node &child = children.next();
144         Node &info_child = info[child.name()];
145         res &= verify_single_table(child, info_child);
146         num_tables++;
147     }
148 
149     // Check if there were actually any children
150     res &= num_tables > 0;
151 
152     if(res)
153     {
154         info["tables"] = num_tables;
155     }
156     utils::log::validation(info, res);
157     return res;
158 }
159 
160 //-----------------------------------------------------------------------------
161 // -- end internal helper functions --
162 //-----------------------------------------------------------------------------
163 
164 //-----------------------------------------------------------------------------
165 // -- begin conduit:: --
166 //-----------------------------------------------------------------------------
167 namespace conduit
168 {
169 
170 //-----------------------------------------------------------------------------
171 // -- begin conduit::blueprint --
172 //-----------------------------------------------------------------------------
173 namespace blueprint
174 {
175 
176 //-----------------------------------------------------------------------------
177 // -- begin conduit::table --
178 //-----------------------------------------------------------------------------
179 namespace table
180 {
181 
182 //-----------------------------------------------------------------------------
verify(const conduit::Node & n,conduit::Node & info)183 bool verify(const conduit::Node &n,
184             conduit::Node &info)
185 {
186     bool res = true;
187     info.reset();
188 
189     if(n.has_child("values"))
190     {
191         res = verify_single_table(n, info);
192     }
193     else
194     {
195         res = verify_many_tables(n, info);
196     }
197     return res;
198 }
199 
200 //-----------------------------------------------------------------------------
verify(const std::string &,const conduit::Node &,conduit::Node & info)201 bool verify(const std::string &,
202             const conduit::Node &,
203             conduit::Node &info)
204 {
205     // Table doesn't currently provide any nested protocols
206 
207     info.reset();
208     utils::log::validation(info,false);
209     return false;
210 }
211 
212 }
213 //-----------------------------------------------------------------------------
214 // -- end conduit::table --
215 //-----------------------------------------------------------------------------
216 
217 }
218 //-----------------------------------------------------------------------------
219 // -- end conduit::blueprint --
220 //-----------------------------------------------------------------------------
221 
222 }
223 //-----------------------------------------------------------------------------
224 // -- end conduit:: --
225 //-----------------------------------------------------------------------------
226