Flow123d  master-f44eb46
reader_internal_base.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file reader_internal_base.cc
15  * @brief
16  */
17 
18 
19 #include "system/asserts.hh" // for Assert, ASSERT
20 #include "system/file_path.hh" // for FilePath, File...
21 #include "system/logger.hh" // for operator<<
22 
25 #include "input/input_type.hh"
26 
27 namespace Input {
28 
29 using namespace std;
30 
31 /*******************************************************************
32  * implementation of ReaderInternalBase
33  */
34 
36 {}
37 
39 {
40  ASSERT_PTR(type).error("Can not dispatch, NULL pointer to TypeBase.");
41 
42  // find reference node, if doesn't exist return NULL
43  PathBase * ref_path = p.find_ref_node();
44  if (ref_path) {
45  // todo: mark passed references and check cyclic references
46 
47  // dereference and take data from there
48  StorageBase * storage = make_storage( *ref_path, type );
49  delete ref_path;
50  return storage;
51  }
52 
53  // dispatch types - complex types
54  if (typeid(*type) == typeid(Type::Tuple)) {
55  return make_sub_storage(p, static_cast<const Type::Tuple *>(type) );
56  } else
57  if (typeid(*type) == typeid(Type::Record)) {
58  return make_sub_storage(p, static_cast<const Type::Record *>(type) );
59  } else
60  if (typeid(*type) == typeid(Type::Array)) {
61  return make_sub_storage(p, static_cast<const Type::Array *>(type) );
62  } else {
63  const Type::Abstract * abstract_record_type = dynamic_cast<const Type::Abstract *>(type);
64  if (abstract_record_type != NULL ) return make_sub_storage(p, abstract_record_type );
65  }
66 
67  // return Null storage if there is null on the current location
68  if (p.is_null_type()) {
69  return new StorageNull();
70  }
71 
72  // dispatch types - scalar types
73  if (typeid(*type) == typeid(Type::Integer)) {
74  return make_sub_storage(p, static_cast<const Type::Integer *>(type) );
75  } else
76  if (typeid(*type) == typeid(Type::Double)) {
77  return make_sub_storage(p, static_cast<const Type::Double *>(type) );
78  } else
79  if (typeid(*type) == typeid(Type::Bool)) {
80  return make_sub_storage(p, static_cast<const Type::Bool *>(type) );
81  } else
82  if (typeid(*type) == typeid(Type::Selection)) {
83  return make_sub_storage(p, static_cast<const Type::Selection *>(type) );
84  } else {
85  const Type::String * string_type = dynamic_cast<const Type::String *>(type);
86  if (string_type != NULL ) return make_sub_storage(p, string_type );
87 
88  // default -> error
89  THROW( Type::ExcUnknownDescendant() << Type::EI_TypeName(typeid(type).name()) );
90  }
91 
92  return new StorageNull();
93 }
94 
96 {
97  // control test, check correct tag (or TYPE key) if Record is derived from Abstract
98  string record_name_from_tag = p.get_record_tag();
99  if (record_name_from_tag == "include") {
100  return make_include_storage(p, record);
101  } else if (record_name_from_tag == "include_csv") {
102  THROW( ExcForbiddenTag() << EI_Tag("include_csv")
103  << EI_Specification("can be used only with arrays.") << EI_Address(p.as_string()) );
104  } else {
105  if ( record_name_from_tag != "" ) {
106  ASSERT(record_name_from_tag == record->type_name())(record_name_from_tag)(record->type_name()).error("Inconsistent tag of record.");
107  }
108  std::set<string> keys_to_process;
109  bool effectively_null = p.is_effectively_null();
110  if ( p.get_record_key_set(keys_to_process) || effectively_null ) {
111  std::set<string>::iterator set_it;
112 
113  /*Type::Record::KeyIter key_it;
114  if ( record->has_key_iterator("TYPE", key_it) && record->auto_conversion_key_iter() != record->end() ) {
115  PathBase *type_path = p->clone();
116  if ( type_path.down( "TYPE" ) ) {
117  try {
118  ASSERT( type_path.get_string_value() == record->type_name() )(type_path.get_string_value())(record->type_name())
119  .error("Invalid value of TYPE key of record");
120  make_storage(type_path, key_it->type_.get() )->get_int();
121  } catch(Type::Selection::ExcSelectionKeyNotFound &e) {
122  return record_automatic_conversion(p, record);
123  }
124  }
125  else { // automatic conversion
126  return record_automatic_conversion(p, record);
127  }
128  }*/
129 
130  StorageArray *storage_array = new StorageArray(record->size());
131  // check individual keys
132  for( Type::Record::KeyIter it= record->begin(); it != record->end(); ++it) {
133  // remove processed key from keys_to_process
134  set_it = keys_to_process.find(it->key_);
135  if (set_it != keys_to_process.end()) {
136  keys_to_process.erase(set_it);
137  }
138 
139  if ( !effectively_null && p.down(it->key_, it->key_index) ) {
140  // key on input => check & use it
141  // check for obsolete key
142 
143  auto obsolete_it = it->attributes.find( Type::Attribute::obsolete() );
144  if ( obsolete_it != it->attributes.end()) {
145  WarningOut() << "Usage of the obsolete key: '" << it->key_ << "'\n" << obsolete_it -> second;
146  }
147 
148  StorageBase *storage = make_storage(p, it->type_.get());
149  if ( (typeid(*storage) == typeid(StorageNull)) && it->default_.has_value_at_declaration() ) {
150  delete storage;
151  storage = make_storage_from_default( it->default_.value(), it->type_ );
152  }
153  storage_array->new_item( it->key_index, storage );
154  p.up();
155  } else {
156  // key not on input
157  if (it->default_.is_obligatory() ) {
158  this->generate_input_error(p, record, "Missing obligatory key '"+ it->key_ +"'.", false);
159  } else if (it->default_.has_value_at_declaration() ) {
160  storage_array->new_item(it->key_index,
161  make_storage_from_default( it->default_.value(), it->type_ ) );
162  } else { // defalut - optional or default at read time
163  // set null
164  storage_array->new_item(it->key_index, new StorageNull() );
165  }
166  }
167  }
168 
169  for( set_it = keys_to_process.begin(); set_it != keys_to_process.end(); ++set_it) {
170  WarningOut() << "Unprocessed key '" << (*set_it) << "' in " << record->class_name()
171  << " '" << p.as_string() << "'." << std::endl;
172  }
173 
174  return storage_array;
175 
176  } else { // automatic conversion
177  return record_automatic_conversion(p, record);
178  }
179  // possibly construction of reduced record
180  }
181 
182  return NULL;
183 }
184 
186 {
187  int arr_size;
188  if ( (arr_size = p.get_array_size()) != -1 ) {
189 
190  StorageArray *storage_array = new StorageArray(tuple->size());
191  // check individual keys
192  for ( Type::Record::KeyIter it= tuple->begin(); it != tuple->end(); ++it) {
193  if ( p.down(it->key_index) ) {
194  // key on input => check & use it
195  StorageBase *storage = make_storage(p, it->type_.get());
196  if ( (typeid(*storage) == typeid(StorageNull)) && it->default_.has_value_at_declaration() ) {
197  delete storage;
198  storage = make_storage_from_default( it->default_.value(), it->type_ );
199  }
200  storage_array->new_item( it->key_index, storage );
201  p.up();
202  } else {
203  // key not on input
204  if (it->default_.is_obligatory() ) {
205  stringstream ss;
206  ss << "Too small size of '" << p.get_node_type(ValueTypes::array_type) << "' defining Tuple with "
207  << tuple->obligatory_keys_count() << " obligatory keys.";
208  this->generate_input_error(p, tuple, ss.str(), false);
209  } else if (it->default_.has_value_at_declaration() ) {
210  storage_array->new_item(it->key_index,
211  make_storage_from_default( it->default_.value(), it->type_ ) );
212  } else { // default - optional or default at read time
213  // set null
214  storage_array->new_item(it->key_index, new StorageNull() );
215  }
216  }
217  }
218 
219  if ( arr_size > (int)tuple->size() ) {
220  WarningOut().fmt("Unprocessed keys in tuple '{}', tuple has {} keys but the input is specified by {} values.\n",
221  p.as_string().c_str(), tuple->size(), arr_size );
222  }
223 
224  return storage_array;
225 
226  } else {
227  return make_sub_storage(p, static_cast<const Type::Record *>(tuple) );
228  }
229 }
230 
232 {
233  string record_tag = p.get_record_tag();
234  if (record_tag == "") {
235  if ( ! abstr_rec->get_selection_default().has_value_at_declaration() ) {
236  this->generate_input_error(p, abstr_rec, "Can not determine type of the Abstract.", true);
237  } else { // auto conversion
238  return abstract_automatic_conversion(p, abstr_rec);
239  }
240  } else if (record_tag.substr(0,8) == "include:") { // include of abstract is predetermined with tag '!include:record_name'
241  string record_name = record_tag.substr(8);
242  try {
243  return make_include_storage(p, &( abstr_rec->get_descendant(record_name) ) );
244  } catch (Type::Selection::ExcSelectionKeyNotFound &exc) {
245  this->generate_input_error(p, abstr_rec, "Wrong value '" + record_tag + "' of the Selection.", false);
246  }
247  } else if ((record_tag == "include") || (record_tag == "include_csv")) { // simple '!include' tag is forbidden
248  THROW( ExcForbiddenTag() << EI_Tag(record_tag)
249  << EI_Specification("can't be used with abstract type.") << EI_Address(p.as_string()) );
250  } else {
251  try {
252  return make_sub_storage(p, &( abstr_rec->get_descendant(record_tag) ) );
253  } catch (Type::Selection::ExcSelectionKeyNotFound &exc) {
254  this->generate_input_error(p, abstr_rec, "Wrong value '" + record_tag + "' of the Selection.", false);
255  }
256  }
257  return NULL;
258 }
259 
261 {
262  Type::Record::KeyIter auto_key_it = record->auto_conversion_key_iter();
263  if ( auto_key_it != record->end() ) {
264  try {
265  StorageArray *storage_array = new StorageArray(record->size());
266  for( Type::Record::KeyIter it= record->begin(); it != record->end(); ++it) {
267  if ( it == auto_key_it ) {
268  // one key is initialized by input
269  storage_array->new_item(it->key_index, make_storage(p, it->type_.get()) );
270  } else if (it->default_.has_value_at_declaration() ) {
271  // other key from default values
272  storage_array->new_item(it->key_index,
273  make_storage_from_default( it->default_.value(), it->type_ ) );
274  } else { // defalut - optional or default at read time
275  ASSERT(! it->default_.is_obligatory())(it->key_).error("Obligatory key in auto-convertible Record.");
276  // set null
277  storage_array->new_item(it->key_index, new StorageNull() );
278  }
279  }
280 
281  return storage_array;
282  } catch (ExcInputError &e ) {
283  THROW( ExcAutomaticConversionError() << EI_RecordName(record->type_name())
284  << EI_InputErrorMessage(e.what()) );
285  }
286 
287  } else {
288  this->generate_input_error(p, record, "The value should be '" + p.get_node_type(ValueTypes::obj_type) + "', but we found: ", true);
289  }
290 
291  return NULL;
292 }
293 
295 {
296  // perform automatic conversion
297  const Type::Record *default_child = abstr_rec->get_default_descendant();
298  if (! default_child)
299  this->generate_input_error(p, abstr_rec, "Auto conversion of Abstract not allowed.\n", false);
300  return make_sub_storage(p, default_child );
301 }
302 
304 {
305  ASSERT(p.is_array_type()).error();
306 
307  if ( array->match_size( arr_size ) ) {
308  // copy the array and check type of values
309  StorageArray *storage_array = new StorageArray(arr_size);
310  for( int idx=0; idx < arr_size; idx++) {
311  p.down(idx);
312  const Type::TypeBase &sub_type = array->get_sub_type();
313  storage_array->new_item(idx, make_storage(p, &sub_type) );
314  p.up();
315  }
316  return storage_array;
317 
318  } else {
319  stringstream ss;
320  ss << "Do not fit the size " << arr_size << " of the Array.";
321  this->generate_input_error(p, array, ss.str(), false);
322  }
323 
324  return NULL; // suppress warning for non-void function
325 }
326 
327 StorageBase * ReaderInternalBase::make_storage_from_default(const string &dflt_str, std::shared_ptr<Type::TypeBase> type) {
328  try {
329  // default strings must be valid JSON
330  Type::Default dflt(dflt_str);
331  return dflt.get_storage(type);
332 
333  } catch (Input::Type::ExcWrongDefault & e) {
334  // message to distinguish exceptions thrown during Default value check at declaration
335  e << Type::EI_Desc("Wrong default value while reading an input stream:\n");
336  e << Type::EI_KeyName("UNKNOWN KEY");
337  throw;
338  } catch (Input::Type::ExcWrongDefaultJSON & e) {
339  e << Type::EI_KeyName("UNKNOWN KEY");
340  throw;
341  }
342 
343  return NULL;
344 }
345 
347 {
348  std::string included_path;
349  if ( p.is_record_type() ) {
350  // include is set as record with tag and file key
351  if ( p.down("file") ) {
352  included_path = get_included_file(p);
353  p.up();
354  } else {
355  this->generate_input_error(p, type, "Missing key 'file' defines including input file.", false);
356  }
357  } else {
358  // include is set only with name of file (similarly as auto conversion)
359  // this case may occur only for YAML input
360  included_path = get_included_file(p);
361  }
362 
363  FilePath fpath(included_path, FilePath::FileType::input_file);
364  ReaderToStorage include_reader(fpath, *(const_cast<Type::TypeBase *>(type)) );
365  return include_reader.get_storage();
366 }
367 
369 {
370  bool value;
371  try {
372  value = p.get_bool_value();
373  }
374  catch (ExcInputError & e) {
375  complete_input_error(e, p, ValueTypes::bool_type);
376  e << EI_InputType(type->desc());
377  throw;
378  }
379 
380  return value;
381 }
382 
384 {
385  std::int64_t value;
386  try {
387  value = p.get_int_value();
388  }
389  catch (ExcInputError & e) {
390  complete_input_error(e, p, ValueTypes::int_type);
391  e << EI_InputType(type->desc());
392  throw;
393  }
394 
395  return value;
396 }
397 
399 {
400  double value;
401  try {
402  value = p.get_double_value();
403  }
404  catch (ExcInputError & e) {
405  complete_input_error(e, p, ValueTypes::real_type);
406  e << EI_InputType(type->desc());
407  throw;
408  }
409 
410  return value;
411 }
412 
414 {
415  string value;
416  try {
417  value = p.get_string_value();
418  } catch (ExcInputError & e) {
419  complete_input_error(e, p, ValueTypes::str_type);
420  e << EI_InputType(type->desc());
421  throw;
422  }
423 
424  return value;
425 }
426 
428 {
429  try {
430  return p.get_string_value();
431  }
432  catch (ExcInputError & e) {
433  complete_input_error(e, p, ValueTypes::str_type);
434  e << EI_InputType("path to included file");
435  throw;
436  }
437 }
438 
439 void ReaderInternalBase::generate_input_error(PathBase &p, const Type::TypeBase *type, std::string spec, bool add_type)
440 {
441  if (add_type)
442  THROW( ExcInputError() << EI_Specification(spec) << EI_JSON_Type( p.get_node_type(p.get_node_type_index()) )
443  << EI_ErrorAddress(p.as_string()) << EI_InputType(type->desc()) );
444  else
445  THROW( ExcInputError() << EI_Specification(spec) << EI_JSON_Type( "" )
446  << EI_ErrorAddress(p.as_string()) << EI_InputType(type->desc()) );
447 }
448 
449 void ReaderInternalBase::complete_input_error(ExcInputError & e, PathBase &p, ValueTypes value_type)
450 {
451  e << EI_Specification("The value should be '" + p.get_node_type(value_type) + "', but we found: ");
452  e << EI_ErrorAddress(p.as_string());
453  e << EI_JSON_Type( p.get_node_type(p.get_node_type_index()) );
454 }
455 
456 
457 } // namespace Input
Definitions of ASSERTS.
#define ASSERT(expr)
Definition: asserts.hh:351
#define ASSERT_PTR(ptr)
Definition of assert macro checking non-null pointer (PTR) only for debug mode.
Definition: asserts.hh:341
Dedicated class for storing path to input and output files.
Definition: file_path.hh:54
Base abstract class used by ReaderToStorage class to iterate over the input tree.
Definition: path_base.hh:41
virtual PathBase * find_ref_node()=0
Check if current head node is containing one key REF of type string.
virtual bool get_bool_value() const =0
Get boolean value of head node or throw exception.
virtual bool is_effectively_null() const =0
Check empty Input Type Record, necessary for correct proccess of YAML output, for JSON has no effect.
virtual bool is_array_type() const =0
Check if type of head node is array.
std::string get_node_type(unsigned int type_idx) const
Get short string description of node type, method is used for printout of messages.
Definition: path_base.cc:64
virtual unsigned int get_node_type_index() const =0
Get index of head type, value corresponds with order in json_type_names vector.
virtual double get_double_value() const =0
Get double value of head node or throw exception.
virtual bool is_null_type() const =0
Check if type of head node is null.
virtual bool is_record_type() const =0
Check if type of head node is record.
virtual std::string get_record_tag() const =0
Gets value of the record tag, which determines its type.
virtual std::string get_string_value() const =0
Get string value of head node or throw exception.
virtual std::int64_t get_int_value() const =0
Get integer value of head node or throw exception.
virtual void up()=0
Return one level up in the hierarchy.
virtual int get_array_size() const =0
Get size of array (sequence type), if object is not array return -1.
virtual bool down(unsigned int index)=0
Dive one level down into path hierarchy.
virtual bool get_record_key_set(std::set< std::string > &) const =0
Get set of keys of head type record, if head type is not record return false.
std::string as_string() const
Returns string address of current position.
Definition: path_base.cc:48
std::string get_included_file(PathBase &p)
Helper method. Get string value of included file or throw exception if reading failed.
StorageBase * make_include_storage(PathBase &p, const Type::TypeBase *type)
Create storage of included YAML or JSON input file.
StorageBase * make_array_storage(PathBase &p, const Type::Array *array, int arr_size)
Create storage of Type::Array with given size.
double read_double_value(PathBase &p, const Type::TypeBase *type)
Read double value from path.
StorageBase * record_automatic_conversion(PathBase &p, const Type::Record *record)
Apply automatic conversion of Type::Record type.
std::int64_t read_int_value(PathBase &p, const Type::TypeBase *type)
Read integer value from path.
StorageBase * abstract_automatic_conversion(PathBase &p, const Type::Abstract *abstr_rec)
Apply automatic conversion of Type::Abstract type.
bool read_bool_value(PathBase &p, const Type::TypeBase *type)
Read boolean value from path.
StorageBase * make_storage_from_default(const string &dflt_str, std::shared_ptr< Type::TypeBase > type)
Dispatch according to type and create corresponding storage from the given string.
std::string read_string_value(PathBase &p, const Type::TypeBase *type)
Read string value from path.
void generate_input_error(PathBase &p, const Type::TypeBase *type, std::string spec, bool add_type)
Generate ExcInputError.
StorageBase * make_storage(PathBase &p, const Type::TypeBase *type)
Create storage of given type.
StorageBase * make_sub_storage(PathBase &p, const Type::Record *record)
Create storage of Type::Record type. Common method of all descendants.
void complete_input_error(ExcInputError &e, PathBase &p, ValueTypes value_type)
Complete specification, error address and JSON type error tags to ExcInputError.
Reader for (slightly) modified input files.
StorageBase * get_storage()
Getter for root of the storage tree.
void new_item(unsigned int index, StorageBase *item)
Definition: storage.cc:107
Base class for nodes of a data storage tree.
Definition: storage.hh:68
Class for declaration of polymorphic Record.
const Record & get_descendant(const string &name) const
Returns reference to the inherited Record with given name.
const Record * get_default_descendant() const
Returns default descendant.
Default & get_selection_default() const
Class for declaration of inputs sequences.
Definition: type_base.hh:339
static string obsolete()
Class for declaration of the input of type Bool.
Definition: type_base.hh:452
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:61
Input::StorageBase * get_storage(std::shared_ptr< TypeBase > type) const
Return storage_, if storage_ is NULL, call check_validity method.
Definition: type_record.cc:86
bool has_value_at_declaration() const
Returns true if the default value is or will be available when someone tries to read the value.
Definition: type_record.hh:132
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:534
Class for declaration of the integral input data.
Definition: type_base.hh:483
Record type proxy class.
Definition: type_record.hh:182
unsigned int size() const
Returns number of keys in the Record.
Definition: type_record.hh:602
string type_name() const override
Implements Type::TypeBase::type_name.
Definition: type_record.cc:317
virtual string class_name() const override
Override Type::TypeBase::class_name.
Definition: type_record.cc:323
KeyIter begin() const
Container-like access to the keys of the Record.
Definition: type_record.hh:579
std::vector< struct Key >::const_iterator KeyIter
Public typedef of constant iterator into array of keys.
Definition: type_record.hh:216
KeyIter end() const
Container-like access to the keys of the Record.
Definition: type_record.hh:587
KeyIter auto_conversion_key_iter() const
Returns iterator to auto-conversion key.
Definition: type_record.cc:335
Template for classes storing finite set of named values.
Class for declaration of the input data that are in string format.
Definition: type_base.hh:582
Tuple type proxy class.
Definition: type_tuple.hh:45
unsigned int obligatory_keys_count() const
Return count of obligatory keys.
Definition: type_tuple.cc:139
Base of classes for declaring structure of the input data.
Definition: type_base.hh:92
string desc() const
Returns string with Type extensive documentation.
Definition: type_base.cc:99
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53
static constexpr bool value
Definition: json.hpp:87
#define WarningOut()
Macro defining 'warning' record of log.
Definition: logger.hh:278
Array< double > array
Definition: armor.hh:890
Abstract linear system class.
Definition: balance.hh:40
ValueTypes
Enum of possible input types.