LCOV - code coverage report
Current view: top level - lib - db.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 176 176 100.0 %
Date: 2014-11-22 Functions: 22 22 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* db.cpp -- written by Alexis WILKE for Made to Order Software Corp. (c) 2005-2014 */
       2             : 
       3             : /*
       4             : 
       5             : Copyright (c) 2005-2014 Made to Order Software Corp.
       6             : 
       7             : http://snapwebsites.org/project/as2js
       8             : 
       9             : Permission is hereby granted, free of charge, to any
      10             : person obtaining a copy of this software and
      11             : associated documentation files (the "Software"), to
      12             : deal in the Software without restriction, including
      13             : without limitation the rights to use, copy, modify,
      14             : merge, publish, distribute, sublicense, and/or sell
      15             : copies of the Software, and to permit persons to whom
      16             : the Software is furnished to do so, subject to the
      17             : following conditions:
      18             : 
      19             : The above copyright notice and this permission notice
      20             : shall be included in all copies or substantial
      21             : portions of the Software.
      22             : 
      23             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
      24             : ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
      25             : LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
      26             : FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
      27             : EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
      28             : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      29             : WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
      30             : ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      31             : SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      32             : SOFTWARE.
      33             : 
      34             : */
      35             : 
      36             : #include    "db.h"  // 100% private header
      37             : 
      38             : #include    "as2js/exceptions.h"
      39             : #include    "as2js/message.h"
      40             : 
      41             : 
      42             : namespace as2js
      43             : {
      44             : 
      45             : 
      46             : 
      47             : 
      48             : 
      49        3132 : Database::Element::Element(String const& element_name, JSON::JSONValue::pointer_t element)
      50             :     : f_element_name(element_name)
      51             :     //, f_type("") -- auto-init
      52             :     //, f_filename("") -- auto-init
      53             :     //, f_line(1) -- auto-init
      54        3133 :     , f_element(element)
      55             : {
      56             :     // verify the type, but we already tested before creating this object
      57        3132 :     JSON::JSONValue::type_t type(f_element->get_type());
      58        3132 :     if(type != JSON::JSONValue::type_t::JSON_TYPE_OBJECT)
      59             :     {
      60           1 :         throw exception_internal_error("an element cannot be created with a JSON value which has a type other than Object");
      61             :     }
      62             : 
      63             :     // we got a valid database element object
      64        3131 :     JSON::JSONValue::object_t const& obj(f_element->get_object());
      65        9452 :     for(JSON::JSONValue::object_t::const_iterator it(obj.begin()); it != obj.end(); ++it)
      66             :     {
      67        6321 :         JSON::JSONValue::type_t const sub_type(it->second->get_type());
      68        6321 :         String const field_name(it->first);
      69        6321 :         if(field_name == "type")
      70             :         {
      71        2107 :             if(sub_type != JSON::JSONValue::type_t::JSON_TYPE_STRING)
      72             :             {
      73           1 :                 Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_DATABASE, it->second->get_position());
      74           1 :                 msg << "The type of an element in the database has to be a string.";
      75             :             }
      76             :             else
      77             :             {
      78        2106 :                 f_type = it->second->get_string();
      79             :             }
      80             :         }
      81        4214 :         else if(field_name == "filename")
      82             :         {
      83        2107 :             if(sub_type != JSON::JSONValue::type_t::JSON_TYPE_STRING)
      84             :             {
      85           1 :                 Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_DATABASE, it->second->get_position());
      86           1 :                 msg << "The filename of an element in the database has to be a string.";
      87             :             }
      88             :             else
      89             :             {
      90        2106 :                 f_filename = it->second->get_string();
      91             :             }
      92             :         }
      93        2107 :         else if(field_name == "line")
      94             :         {
      95        2107 :             if(sub_type != JSON::JSONValue::type_t::JSON_TYPE_INT64)
      96             :             {
      97           1 :                 Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_DATABASE, it->second->get_position());
      98           1 :                 msg << "The line of an element in the database has to be an integer.";
      99             :             }
     100             :             else
     101             :             {
     102        2106 :                 f_line = static_cast<Position::counter_t>(it->second->get_int64().get());
     103             :             }
     104             :         }
     105             :         // else -- TBD: should we err on unknown fields?
     106        6321 :     }
     107        3131 : }
     108             : 
     109             : 
     110         124 : void Database::Element::set_type(String const& type)
     111             : {
     112         124 :     f_type = type;
     113         124 :     f_element->set_member("type", JSON::JSONValue::pointer_t(new JSON::JSONValue(f_element->get_position(), f_type)));
     114         124 : }
     115             : 
     116             : 
     117         124 : void Database::Element::set_filename(String const& filename)
     118             : {
     119         124 :     f_filename = filename;
     120         124 :     f_element->set_member("filename", JSON::JSONValue::pointer_t(new JSON::JSONValue(f_element->get_position(), f_filename)));
     121         124 : }
     122             : 
     123             : 
     124         124 : void Database::Element::set_line(Position::counter_t line)
     125             : {
     126         124 :     f_line = line;
     127         124 :     Int64 integer(f_line);
     128         124 :     f_element->set_member("line", JSON::JSONValue::pointer_t(new JSON::JSONValue(f_element->get_position(), integer)));
     129         124 : }
     130             : 
     131             : 
     132        9169 : String Database::Element::get_element_name() const
     133             : {
     134        9169 :     return f_element_name;
     135             : }
     136             : 
     137             : 
     138        3207 : String Database::Element::get_type() const
     139             : {
     140        3207 :     return f_type;
     141             : }
     142             : 
     143             : 
     144        3207 : String Database::Element::get_filename() const
     145             : {
     146        3207 :     return f_filename;
     147             : }
     148             : 
     149             : 
     150        3207 : Position::counter_t Database::Element::get_line() const
     151             : {
     152        3207 :     return f_line;
     153             : }
     154             : 
     155             : 
     156             : 
     157             : 
     158             : 
     159             : 
     160             : 
     161         108 : Database::Package::Package(String const& package_name, JSON::JSONValue::pointer_t package)
     162             :     : f_package_name(package_name)
     163         109 :     , f_package(package)
     164             : {
     165             :     // verify the type, but we already tested before creatin this object
     166         108 :     JSON::JSONValue::type_t type(f_package->get_type());
     167         108 :     if(type != JSON::JSONValue::type_t::JSON_TYPE_OBJECT)
     168             :     {
     169           1 :         throw exception_internal_error("a package cannot be created with a JSON value which has a type other than Object");
     170             :     }
     171             : 
     172             :     // we got a valid database package object
     173         107 :     JSON::JSONValue::object_t const& obj(f_package->get_object());
     174        1116 :     for(JSON::JSONValue::object_t::const_iterator it(obj.begin()); it != obj.end(); ++it)
     175             :     {
     176             :         // the only type of value that we expect are objects within the
     177             :         // main object; each one represents a package
     178        1009 :         JSON::JSONValue::type_t const sub_type(it->second->get_type());
     179        1009 :         if(sub_type != JSON::JSONValue::type_t::JSON_TYPE_OBJECT)
     180             :         {
     181           3 :             Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_DATABASE, it->second->get_position());
     182           3 :             msg << "A database is expected to be an object of object packages composed of object elements.";
     183             :         }
     184             :         else
     185             :         {
     186        1006 :             String element_name(it->first);
     187        2012 :             Element::pointer_t e(new Element(element_name, it->second));
     188        2012 :             f_elements[element_name] = e;
     189             :         }
     190             :     }
     191         107 : }
     192             : 
     193             : 
     194         100 : String Database::Package::get_package_name() const
     195             : {
     196         100 :     return f_package_name;
     197             : }
     198             : 
     199             : 
     200        3000 : Database::element_vector_t Database::Package::find_elements(String const& pattern) const
     201             : {
     202        3000 :     element_vector_t found;
     203       33000 :     for(auto it(f_elements.begin()); it != f_elements.end(); ++it)
     204             :     {
     205       30000 :         if(match_pattern(it->first, pattern))
     206             :         {
     207        3022 :             found.push_back(it->second);
     208             :         }
     209             :     }
     210        3000 :     return found;
     211             : }
     212             : 
     213             : 
     214        3030 : Database::Element::pointer_t Database::Package::get_element(String const& element_name) const
     215             : {
     216        3030 :     auto it(f_elements.find(element_name));
     217        3030 :     if(it == f_elements.end())
     218             :     {
     219        1024 :         return Element::pointer_t();
     220             :     }
     221             :     // it exists
     222        2006 :     return it->second;
     223             : }
     224             : 
     225             : 
     226        2024 : Database::Element::pointer_t Database::Package::add_element(String const& element_name)
     227             : {
     228        2024 :     auto e(get_element(element_name));
     229        2024 :     if(!e)
     230             :     {
     231             :         // some default position object to attach to the new objects
     232        1024 :         Position pos(f_package->get_position());
     233             : 
     234        2048 :         JSON::JSONValue::object_t obj_element;
     235        2048 :         JSON::JSONValue::pointer_t new_element(new JSON::JSONValue(pos, obj_element));
     236        1024 :         e.reset(new Element(element_name, new_element));
     237        1024 :         f_elements[element_name] = e;
     238             : 
     239        2048 :         f_package->set_member(element_name, new_element);
     240             :     }
     241        2024 :     return e;
     242             : }
     243             : 
     244             : 
     245             : 
     246             : 
     247             : 
     248             : 
     249        9346 : bool Database::load(String const& filename)
     250             : {
     251        9346 :     if(f_json)
     252             :     {
     253             :         // already loaded
     254        9339 :         return f_value.operator bool ();
     255             :     }
     256           7 :     f_filename = filename;
     257           7 :     f_json.reset(new JSON);
     258             : 
     259             :     // test whether the file exists
     260           7 :     FileInput::pointer_t in(new FileInput());
     261           7 :     if(!in->open(filename))
     262             :     {
     263             :         // no db yet... it is okay
     264           2 :         Position pos;
     265           2 :         pos.set_filename(filename);
     266           4 :         JSON::JSONValue::object_t obj_database;
     267           2 :         f_value.reset(new JSON::JSONValue(pos, obj_database));
     268           2 :         f_json->set_value(f_value);
     269           4 :         return true;
     270             :     }
     271             : 
     272             :     // there is a db, load it
     273           5 :     f_value = f_json->parse(in);
     274           5 :     if(!f_value)
     275             :     {
     276           1 :         return false;
     277             :     }
     278             : 
     279           4 :     JSON::JSONValue::type_t type(f_value->get_type());
     280             : 
     281             :     // a 'null' is acceptable, it means the database is currently empty
     282           4 :     if(type == JSON::JSONValue::type_t::JSON_TYPE_NULL)
     283             :     {
     284           1 :         return true;
     285             :     }
     286             : 
     287           3 :     if(type != JSON::JSONValue::type_t::JSON_TYPE_OBJECT)
     288             :     {
     289           1 :         Position pos;
     290           1 :         pos.set_filename(filename);
     291           2 :         Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_DATABASE, pos);
     292           1 :         msg << "A database must be defined as a JSON object, or set to 'null'.";
     293           2 :         return false;
     294             :     }
     295             : 
     296             :     // we found the database object
     297             :     // typedef std::map<String, JSONValue::pointer_t> object_t;
     298           2 :     JSON::JSONValue::object_t const& obj(f_value->get_object());
     299           4 :     for(JSON::JSONValue::object_t::const_iterator it(obj.begin()); it != obj.end(); ++it)
     300             :     {
     301             :         // the only type of value that we expect are objects within the
     302             :         // main object; each one represents a package
     303           3 :         JSON::JSONValue::type_t sub_type(it->second->get_type());
     304           3 :         if(sub_type != JSON::JSONValue::type_t::JSON_TYPE_OBJECT)
     305             :         {
     306           1 :             Position pos;
     307           1 :             pos.set_filename(filename);
     308           2 :             Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_DATABASE, pos);
     309           1 :             msg << "A database is expected to be an object of object packages composed of elements.";
     310           2 :             return false;
     311             :         }
     312             : 
     313           2 :         String package_name(it->first);
     314           4 :         Package::pointer_t p(new Package(package_name, it->second));
     315           2 :         f_packages[package_name] = p;
     316           2 :     }
     317             : 
     318           1 :     return true;
     319             : }
     320             : 
     321             : 
     322             : 
     323           3 : void Database::save() const
     324             : {
     325             :     // if it has been loaded, save it
     326           3 :     if(f_json)
     327             :     {
     328             :         String const header("// Database used by the AS2JS Compiler (as2js)\n"
     329             :                             "//\n"
     330             :                             "// DO NOT EDIT UNLESS YOU KNOW WHAT YOU ARE DOING\n"
     331             :                             "// If you have a problem because of the database, just delete the file\n"
     332             :                             "// and the compiler will re-generate it.\n"
     333             :                             "//\n"
     334             :                             "// Copyright (c) 2005-2014 by Made to Order Software Corp.\n"
     335             :                             "// This file is written in UTF-8\n"
     336             :                             "// You can safely modify it with an editor supporting UTF-8\n"
     337             :                             "// The format is JSON:\n"
     338             :                             "//\n"
     339             :                             "// {\n"
     340             :                             "//   \"package_name\": {\n"
     341             :                             "//     \"element_name\": {\n"
     342             :                             "//       \"filename\": \"<full path filename>\",\n"
     343             :                             "//       \"line\": <line number>,\n"
     344             :                             "//       \"type\": \"<type name>\"\n"
     345             :                             "//     },\n"
     346             :                             "//     <...other elements...>\n"
     347             :                             "//   },\n"
     348             :                             "//   <...other packages...>\n"
     349             :                             "// }\n"
     350           2 :                             "//");
     351           2 :         f_json->save(f_filename, header);
     352             :     }
     353           3 : }
     354             : 
     355             : 
     356           7 : Database::package_vector_t Database::find_packages(String const& pattern) const
     357             : {
     358           7 :     package_vector_t found;
     359          13 :     for(auto it(f_packages.begin()); it != f_packages.end(); ++it)
     360             :     {
     361           6 :         if(match_pattern(it->first, pattern))
     362             :         {
     363           4 :             found.push_back(it->second);
     364             :         }
     365             :     }
     366           7 :     return found;
     367             : }
     368             : 
     369             : 
     370          29 : Database::Package::pointer_t Database::get_package(String const& package_name) const
     371             : {
     372          29 :     auto p(f_packages.find(package_name));
     373          29 :     if(p == f_packages.end())
     374             :     {
     375           6 :         return Database::Package::pointer_t();
     376             :     }
     377          23 :     return p->second;
     378             : }
     379             : 
     380             : 
     381          22 : Database::Package::pointer_t Database::add_package(String const& package_name)
     382             : {
     383          22 :     auto p(get_package(package_name));
     384          22 :     if(!p)
     385             :     {
     386           5 :         if(!f_json)
     387             :         {
     388           1 :             throw exception_internal_error("attempting to add a package to the database before the database was loaded");
     389             :         }
     390             : 
     391             :         // some default position object to attach to the new objects
     392           4 :         Position pos;
     393           4 :         pos.set_filename(f_filename);
     394             : 
     395             :         // create the database object if not there yet
     396           4 :         if(!f_value)
     397             :         {
     398           1 :             JSON::JSONValue::object_t obj_database;
     399           1 :             f_value.reset(new JSON::JSONValue(pos, obj_database));
     400           1 :             f_json->set_value(f_value);
     401             :         }
     402             : 
     403           8 :         JSON::JSONValue::object_t obj_package;
     404           8 :         JSON::JSONValue::pointer_t new_package(new JSON::JSONValue(pos, obj_package));
     405           4 :         p.reset(new Package(package_name, new_package));
     406           4 :         f_packages[package_name] = p;
     407             : 
     408           8 :         f_value->set_member(package_name, new_package);
     409             :     }
     410          21 :     return p;
     411             : }
     412             : 
     413             : 
     414       30806 : bool Database::match_pattern(String const& name, String const& pattern)
     415             : {
     416             :     struct for_sub_function
     417             :     {
     418      398242 :         static bool do_match(as_char_t const *name, as_char_t const *pattern)
     419             :         {
     420      452529 :             for(; *pattern != '\0'; ++pattern, ++name)
     421             :             {
     422      449497 :                 if(*pattern == '*')
     423             :                 {
     424             :                     // quick optimization, remove all the '*' if there are
     425             :                     // multiple (although that should probably be an error!)
     426       13224 :                     do
     427             :                     {
     428       13224 :                         ++pattern;
     429             :                     }
     430       13224 :                     while(*pattern == '*');
     431       13224 :                     if(*pattern == '\0')
     432             :                     {
     433        1415 :                         return true;
     434             :                     }
     435      388245 :                     while(*name != '\0')
     436             :                     {
     437      367436 :                         if(do_match(name, pattern)) // recursive call
     438             :                         {
     439        2809 :                             return true;
     440             :                         }
     441      364627 :                         ++name;
     442             :                     }
     443        9000 :                     return false;
     444             :                 }
     445      436273 :                 if(*name != *pattern)
     446             :                 {
     447      381986 :                     return false;
     448             :                 }
     449             :             }
     450             : 
     451             :             // end of name and pattern must match if you did not
     452             :             // end the pattern with an asterisk
     453        3032 :             return *name == '\0';
     454             :         }
     455             :     };
     456             : 
     457             :     // we want to use a recursive function and use bare pointers
     458             :     // because it really simplifies the algorithm...
     459       30806 :     return for_sub_function::do_match(name.c_str(), pattern.c_str());
     460             : }
     461             : 
     462             : 
     463             : 
     464             : 
     465          63 : }
     466             : // namespace as2js
     467             : 
     468             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.10