// file : odb/context.cxx // copyright : Copyright (c) 2009-2015 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file #include #include // std::toupper #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; static inline void add_space (string& s) { string::size_type n (s.size ()); if (n != 0 && s[n - 1] != ' ') s += ' '; } // // custom_cxx_type // string custom_cxx_type:: translate (string const& val, const cxx_tokens& expr) { // Similar to member_access::translate() and a few other places. // string r; cxx_tokens_lexer l; l.start (expr); string tl; for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;) { // Try to format the expression to resemble the style of the // generated code. // switch (tt) { case CPP_NOT: { add_space (r); r += '!'; break; } case CPP_COMMA: { r += ", "; break; } case CPP_OPEN_PAREN: { if (ptt == CPP_NAME || ptt == CPP_KEYWORD) add_space (r); r += '('; break; } case CPP_CLOSE_PAREN: { r += ')'; break; } case CPP_OPEN_SQUARE: { r += '['; break; } case CPP_CLOSE_SQUARE: { r += ']'; break; } case CPP_OPEN_BRACE: { add_space (r); r += "{ "; break; } case CPP_CLOSE_BRACE: { add_space (r); r += '}'; break; } case CPP_SEMICOLON: { r += ';'; break; } case CPP_ELLIPSIS: { add_space (r); r += "..."; break; } case CPP_PLUS: case CPP_MINUS: { bool unary (ptt != CPP_NAME && ptt != CPP_SCOPE && ptt != CPP_NUMBER && ptt != CPP_STRING && ptt != CPP_CLOSE_PAREN && ptt != CPP_PLUS_PLUS && ptt != CPP_MINUS_MINUS); if (!unary) add_space (r); r += cxx_lexer::token_spelling[tt]; if (!unary) r += ' '; break; } case CPP_PLUS_PLUS: case CPP_MINUS_MINUS: { if (ptt != CPP_NAME && ptt != CPP_CLOSE_PAREN && ptt != CPP_CLOSE_SQUARE) add_space (r); r += cxx_lexer::token_spelling[tt]; break; } case CPP_DEREF: case CPP_DEREF_STAR: case CPP_DOT: case CPP_DOT_STAR: { r += cxx_lexer::token_spelling[tt]; break; } case CPP_STRING: { if (ptt == CPP_NAME || ptt == CPP_KEYWORD || ptt == CPP_STRING || ptt == CPP_NUMBER) add_space (r); r += context::strlit (tl); break; } case CPP_NUMBER: { if (ptt == CPP_NAME || ptt == CPP_KEYWORD || ptt == CPP_STRING || ptt == CPP_NUMBER) add_space (r); r += tl; break; } case CPP_SCOPE: { // Add space except for a few common cases. // if (ptt != CPP_NAME && ptt != CPP_OPEN_PAREN && ptt != CPP_OPEN_SQUARE) add_space (r); r += cxx_lexer::token_spelling[tt]; break; } case CPP_NAME: { // Start of a name. // if (ptt == CPP_NAME || ptt == CPP_KEYWORD || ptt == CPP_STRING || ptt == CPP_NUMBER) add_space (r); r += tl; break; } case CPP_QUERY: { if (ptt == CPP_OPEN_PAREN) { // Get the next token and see if it is ')'. // ptt = tt; tt = l.next (tl); if (tt == CPP_CLOSE_PAREN) r += val; else { add_space (r); r += "? "; } continue; // We have already gotten the next token. } // Fall through. } default: { // Handle CPP_KEYWORD here to avoid a warning (it is not // part of the cpp_ttype enumeration). // if (tt == CPP_KEYWORD) { if (ptt == CPP_NAME || ptt == CPP_KEYWORD || ptt == CPP_STRING || ptt == CPP_NUMBER) add_space (r); r += tl; } else { // All the other operators. // add_space (r); r += cxx_lexer::token_spelling[tt]; r += ' '; } break; } } // // Watch out for the continue statements above if you add any // logic here. // ptt = tt; tt = l.next (tl); } return r; } // // view_object // string view_object:: name () const { if (!alias.empty ()) return alias; return kind == object ? context::class_name (*obj) : tbl_name.string (); } // // member_access // bool member_access:: placeholder () const { for (cxx_tokens::const_iterator i (expr.begin ()), e (expr.end ()); i != e;) { if (i->type == CPP_OPEN_PAREN) { if (++i != e && i->type == CPP_QUERY) { if (++i != e && i->type == CPP_CLOSE_PAREN) return true; } } else ++i; } return false; } string member_access:: translate (string const& obj, string const& val, string const& db) const { if (empty ()) { error (loc) << "non-empty " << kind << " expression required" << endl; throw operation_failed (); } // This code is similar to translate_expression() from relations/source.cxx. // string r; cxx_tokens_lexer l; l.start (expr); string tl; for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;) { // Try to format the expression to resemble the style of the // generated code. // switch (tt) { case CPP_COMMA: { r += ", "; break; } case CPP_OPEN_PAREN: { if (ptt == CPP_NAME || ptt == CPP_KEYWORD) add_space (r); r += '('; break; } case CPP_CLOSE_PAREN: { r += ')'; break; } case CPP_OPEN_SQUARE: { r += '['; break; } case CPP_CLOSE_SQUARE: { r += ']'; break; } case CPP_OPEN_BRACE: { add_space (r); r += "{ "; break; } case CPP_CLOSE_BRACE: { add_space (r); r += '}'; break; } case CPP_SEMICOLON: { r += ';'; break; } case CPP_ELLIPSIS: { add_space (r); r += "..."; break; } case CPP_PLUS: case CPP_MINUS: { bool unary (ptt != CPP_NAME && ptt != CPP_SCOPE && ptt != CPP_NUMBER && ptt != CPP_STRING && ptt != CPP_CLOSE_PAREN && ptt != CPP_PLUS_PLUS && ptt != CPP_MINUS_MINUS); if (!unary) add_space (r); r += cxx_lexer::token_spelling[tt]; if (!unary) r += ' '; break; } case CPP_PLUS_PLUS: case CPP_MINUS_MINUS: { if (ptt != CPP_NAME && ptt != CPP_CLOSE_PAREN && ptt != CPP_CLOSE_SQUARE) add_space (r); r += cxx_lexer::token_spelling[tt]; break; } case CPP_DEREF: case CPP_DEREF_STAR: case CPP_DOT: case CPP_DOT_STAR: { r += cxx_lexer::token_spelling[tt]; break; } case CPP_STRING: { if (ptt == CPP_NAME || ptt == CPP_KEYWORD || ptt == CPP_STRING || ptt == CPP_NUMBER) add_space (r); r += context::strlit (tl); break; } case CPP_NUMBER: { if (ptt == CPP_NAME || ptt == CPP_KEYWORD || ptt == CPP_STRING || ptt == CPP_NUMBER) add_space (r); r += tl; break; } case CPP_SCOPE: { // Add space except for a few common cases. // if (ptt != CPP_NAME && ptt != CPP_OPEN_PAREN && ptt != CPP_OPEN_SQUARE) add_space (r); r += cxx_lexer::token_spelling[tt]; break; } case CPP_NAME: { // Start of a name. // if (ptt == CPP_NAME || ptt == CPP_KEYWORD || ptt == CPP_STRING || ptt == CPP_NUMBER) add_space (r); r += tl; break; } case CPP_NOT: case CPP_QUERY: { if (ptt == CPP_OPEN_PAREN) { // Get the next token and see if it is ')'. // ptt = tt; tt = l.next (tl); if (tt == CPP_CLOSE_PAREN) { if (ptt == CPP_NOT) { if (db.empty ()) { error (loc) << "database instance (!) not available in this " << "context" << endl; throw operation_failed (); } r += db; } else r += val; } else { add_space (r); r += (ptt == CPP_NOT ? "!" : "? "); } continue; // We have already gotten the next token. } // Fall through. } default: { // Handle CPP_KEYWORD here to avoid a warning (it is not // part of the cpp_ttype enumeration). // if (tt == CPP_KEYWORD) { if (ptt == CPP_NAME || ptt == CPP_KEYWORD || ptt == CPP_STRING || ptt == CPP_NUMBER) add_space (r); // Translate 'this'. // r += (tl == "this" ? obj : tl); } else { // All the other operators. // add_space (r); r += cxx_lexer::token_spelling[tt]; r += ' '; } break; } } // // Watch out for the continue statements above if you add any // logic here. // ptt = tt; tt = l.next (tl); } return r; } // Sections. // main_section_type main_section; bool main_section_type:: compare (object_section const& s) const { main_section_type const* ms (dynamic_cast (&s)); return ms != 0 && *this == *ms; } bool user_section:: compare (object_section const& s) const { user_section const* us (dynamic_cast (&s)); return us != 0 && *this == *us; } user_section* user_section:: total_base () const { if (base != 0) { semantics::class_* poly_root (context::polymorphic (*object)); if (poly_root != 0 && poly_root != *object) return base; } return 0; } size_t user_sections:: count (unsigned short f) const { size_t r (0); semantics::class_* poly_root (context::polymorphic (*object)); bool poly_derived (poly_root != 0 && poly_root != object); if (poly_derived && (f & count_total) != 0) r = context::polymorphic_base (*object).get ( "user-sections").count (f); for (const_iterator i (begin ()); i != end (); ++i) { // Skip special sections unless we were explicitly asked to count them. // if (i->special == user_section::special_version && (f & count_special_version) == 0) continue; // Skip non-versioned sections if we are only interested in the // versioned ones. // if ((f & count_versioned_only) != 0 && !context::added (*i->member) && !context::deleted (*i->member)) continue; bool ovd (i->base != 0 && poly_derived); if (i->load != user_section::load_eager) { if (i->load_empty ()) { if ((f & count_load_empty) != 0) { if (ovd) { if ((f & count_override) != 0) { r++; continue; // Count each section only once. } } else { if ((f & count_new) != 0 || (f & count_total) != 0) { r++; continue; } } } } else { if ((f & count_load) != 0) { if (ovd) { if ((f & count_override) != 0) { r++; continue; } } else { if ((f & count_new) != 0 || (f & count_total) != 0) { r++; continue; } } } } } if (i->update_empty ()) { if ((f & count_update_empty) != 0) { if (ovd) { if ((f & count_override) != 0) { r++; continue; } } else { if ((f & count_new) != 0 || (f & count_total) != 0) { r++; continue; } } } } else { if ((f & count_update) != 0) { if (ovd) { if ((f & count_override) != 0) { r++; continue; } } else { if ((f & count_new) != 0 || (f & count_total) != 0) { r++; continue; } } } } if (i->optimistic ()) { if ((f & count_optimistic) != 0) { if (ovd) { if ((f & count_override) != 0) { r++; continue; } } else { if ((f & count_new) != 0 || (f & count_total) != 0) { r++; continue; } } } } } return r; } // // context // namespace { char const* keywords[] = { "NULL", "and", "asm", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "class", "compl", "const", "const_cast", "continue", "default", "delete", "do", "double", "dynamic_cast", "else", "end_eq", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq", "operator", "or", "or_eq", "private", "protected", "public", "register", "reinterpret_cast", "return", "short", "signed", "sizeof", "static", "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" }; } auto_ptr create_context (ostream& os, semantics::unit& unit, options const& ops, features& f, semantics::relational::model* m) { auto_ptr r; switch (ops.database ()[0]) { case database::common: { r.reset (new context (os, unit, ops, f)); break; } case database::mssql: { r.reset (new relational::mssql::context (os, unit, ops, f, m)); break; } case database::mysql: { r.reset (new relational::mysql::context (os, unit, ops, f, m)); break; } case database::oracle: { r.reset (new relational::oracle::context (os, unit, ops, f, m)); break; } case database::pgsql: { r.reset (new relational::pgsql::context (os, unit, ops, f, m)); break; } case database::sqlite: { r.reset (new relational::sqlite::context (os, unit, ops, f, m)); break; } } return r; } context:: ~context () { if (current_ == this) current_ = 0; } context:: context (ostream& os_, semantics::unit& u, options_type const& ops, features_type& f, data_ptr d) : data_ (d ? d : data_ptr (new (shared) data (os_))), extra (data_->extra_), os (data_->os_), unit (u), options (ops), features (f), db (options.database ()[0]), in_comment (data_->in_comment_), exp (data_->exp_), ext (data_->ext_), keyword_set (data_->keyword_set_), include_regex (data_->include_regex_), accessor_regex (data_->accessor_regex_), modifier_regex (data_->modifier_regex_), embedded_schema ( ops.generate_schema () && ops.schema_format ()[db].count (schema_format::embedded)), separate_schema ( ops.generate_schema () && ops.schema_format ()[db].count (schema_format::separate)), multi_static (ops.multi_database () == multi_database::static_), multi_dynamic (ops.multi_database () == multi_database::dynamic), force_versioned (false), top_object (data_->top_object_), cur_object (data_->cur_object_) { assert (current_ == 0); current_ = this; // Write boolean values as true/false. // os.setf (ios_base::boolalpha); // Export control. // if (!ops.export_symbol ()[db].empty ()) exp = ops.export_symbol ()[db] + " "; ext = ops.extern_symbol ()[db]; for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i) data_->keyword_set_.insert (keywords[i]); // SQL name regex. // if (ops.table_regex ().count (db) != 0) { strings const& s (ops.table_regex ()[db]); data_->sql_name_regex_[sql_name_table].assign (s.begin (), s.end ()); } if (ops.column_regex ().count (db) != 0) { strings const& s (ops.column_regex ()[db]); data_->sql_name_regex_[sql_name_column].assign (s.begin (), s.end ()); } if (ops.index_regex ().count (db) != 0) { strings const& s (ops.index_regex ()[db]); data_->sql_name_regex_[sql_name_index].assign (s.begin (), s.end ()); } if (ops.fkey_regex ().count (db) != 0) { strings const& s (ops.fkey_regex ()[db]); data_->sql_name_regex_[sql_name_fkey].assign (s.begin (), s.end ()); } if (ops.sequence_regex ().count (db) != 0) { strings const& s (ops.sequence_regex ()[db]); data_->sql_name_regex_[sql_name_sequence].assign (s.begin (), s.end ()); } if (ops.statement_regex ().count (db) != 0) { strings const& s (ops.statement_regex ()[db]); data_->sql_name_regex_[sql_name_statement].assign (s.begin (), s.end ()); } if (ops.sql_name_regex ().count (db) != 0) { strings const& s (ops.sql_name_regex ()[db]); data_->sql_name_regex_[sql_name_all].assign (s.begin (), s.end ()); } // Include regex. // for (strings::const_iterator i (ops.include_regex ().begin ()); i != ops.include_regex ().end (); ++i) data_->include_regex_.push_back (regexsub (*i)); // Common accessor/modifier naming variants. Try the user-supplied and // more specific ones first. // for (strings::const_iterator i (ops.accessor_regex ().begin ()); i != ops.accessor_regex ().end (); ++i) data_->accessor_regex_.push_back (regexsub (*i)); data_->accessor_regex_.push_back (regexsub ("/(.+)/get_$1/")); // get_foo data_->accessor_regex_.push_back (regexsub ("/(.+)/get\\u$1/")); // getFoo data_->accessor_regex_.push_back (regexsub ("/(.+)/get$1/")); // getfoo data_->accessor_regex_.push_back (regexsub ("/(.+)/$1/")); // foo for (strings::const_iterator i (ops.modifier_regex ().begin ()); i != ops.modifier_regex ().end (); ++i) data_->modifier_regex_.push_back (regexsub (*i)); data_->modifier_regex_.push_back (regexsub ("/(.+)/set_$1/")); // set_foo data_->modifier_regex_.push_back (regexsub ("/(.+)/set\\u$1/")); // setFoo data_->modifier_regex_.push_back (regexsub ("/(.+)/set$1/")); // setfoo data_->modifier_regex_.push_back (regexsub ("/(.+)/$1/")); // foo } context:: context () : data_ (current ().data_), extra (current ().extra), os (current ().os), unit (current ().unit), options (current ().options), features (current ().features), db (current ().db), in_comment (current ().in_comment), exp (current ().exp), ext (current ().ext), keyword_set (current ().keyword_set), include_regex (current ().include_regex), accessor_regex (current ().accessor_regex), modifier_regex (current ().modifier_regex), embedded_schema (current ().embedded_schema), separate_schema (current ().separate_schema), multi_static (current ().multi_static), multi_dynamic (current ().multi_dynamic), force_versioned (current ().force_versioned), top_object (current ().top_object), cur_object (current ().cur_object) { } context* context::current_; semantics::data_member* context:: id (data_member_path const& mp) { semantics::data_member* idf (mp.front ()); if (!id (*idf)) return 0; // This is for special ids, such as polymorphic-ref, which // don't have "id-member" set (and we want to keep it that // way since it is not really a full-fledged id). // if (idf->get ("id").empty ()) // Not a nested id. return idf; const data_member_path& id ( *id_member ( dynamic_cast (idf->scope ()))); // Now we need to make sure id is a prefix of mp; // return mp.sub (id) ? idf : 0; } semantics::data_member* context:: object_pointer (data_member_path const& mp) { for (data_member_path::const_reverse_iterator i (mp.rbegin ()); i != mp.rend (); ++i) { if (object_pointer (utype (**i))) return *i; } return 0; } bool context:: readonly (data_member_path const& mp, data_member_scope const& ms) { assert (mp.size () == ms.size ()); data_member_scope::const_reverse_iterator si (ms.rbegin ()); for (data_member_path::const_reverse_iterator pi (mp.rbegin ()); pi != mp.rend (); ++pi, ++si) { semantics::data_member& m (**pi); if (m.count ("readonly")) return true; // Check if any of the classes in the inheritance chain for the // class containing this member are readonly. // class_inheritance_chain const& ic (*si); assert (ic.back () == &m.scope ()); for (class_inheritance_chain::const_reverse_iterator ci (ic.rbegin ()); ci != ic.rend (); ++ci) { semantics::class_& c (**ci); if (c.count ("readonly")) return true; } } return false; } bool context:: readonly (semantics::data_member& m) { if (m.count ("readonly")) return true; // Check if the whole class (object or composite value) is marked // as readonly. // if (m.scope ().count ("readonly")) return true; return false; } bool context:: null (data_member_path const& mp) const { // Outer members can override the null-ability of the inner ones. So // start from the most outer member. // for (data_member_path::const_iterator i (mp.begin ()); i != mp.end (); ++i) { if (null (**i)) return true; } return false; } bool context:: null (semantics::data_member& m) const { semantics::names* hint; semantics::type& t (utype (m, hint)); if (object_pointer (t)) { // By default pointers can be null. // if (m.count ("null")) return true; if (!m.count ("not-null")) { if (t.count ("null")) return true; if (!t.count ("not-null")) return true; } return false; } else { // Everything else by default is not null. // if (m.count ("null")) return true; if (!m.count ("not-null")) { if (t.count ("null")) return true; if (!t.count ("not-null")) { semantics::type* pt; // Check if this type is a wrapper. // if (t.get ("wrapper")) { // First see if it is null by default. // if (t.get ("wrapper-null-handler") && t.get ("wrapper-null-default")) return true; // Otherwise, check the wrapped type. // pt = t.get ("wrapper-type"); hint = t.get ("wrapper-hint"); pt = &utype (*pt, hint); if (pt->count ("null")) return true; if (pt->count ("not-null")) return false; } else pt = &t; } } return false; } } bool context:: null (semantics::data_member& m, string const& kp) const { if (kp.empty ()) return null (m); semantics::type& c (utype (m)); semantics::type& t (utype (m, kp)); if (object_pointer (t)) { if (m.count (kp + "-null")) return true; if (!m.count (kp + "-not-null")) { if (c.count (kp + "-null")) return true; if (!c.count (kp + "-not-null")) { if (t.count ("null")) return true; if (!t.count ("not-null")) { return true; } } } return false; } else { if (m.count (kp + "-null")) return true; if (!m.count (kp + "-not-null")) { if (c.count (kp + "-null")) return true; if (!c.count (kp + "-not-null")) { if (t.count ("null")) return true; if (!t.count ("not-null")) { semantics::type* pt; // Check if this type is a wrapper. // if (t.get ("wrapper")) { // First see if it is null by default. // if (t.get ("wrapper-null-handler") && t.get ("wrapper-null-default")) return true; // Otherwise, check the wrapped type. // pt = t.get ("wrapper-type"); pt = &utype (*pt); if (pt->count ("null")) return true; if (pt->count ("not-null")) return false; } else pt = &t; } } } return false; } } size_t context:: polymorphic_depth (semantics::class_& c) { if (c.count ("polymorphic-depth")) return c.get ("polymorphic-depth"); // Calculate our hierarchy depth (number of classes). // using semantics::class_; class_* root (polymorphic (c)); assert (root != 0); size_t r (1); // One for the root. for (class_* b (&c); b != root; b = &polymorphic_base (*b)) ++r; c.set ("polymorphic-depth", r); return r; } context::class_kind_type context:: class_kind (semantics::class_& c) { if (object (c)) return class_object; else if (view (c)) return class_view; else if (composite (c)) return class_composite; else return class_other; } string context:: class_name (semantics::class_& c) { return c.is_a () ? c.get ("tree-hint")->name () : c.name (); } string context:: class_fq_name (semantics::class_& c) { return c.is_a () ? c.fq_name (c.get ("tree-hint")) : c.fq_name (); } semantics::scope& context:: class_scope (semantics::class_& c) { return c.is_a () ? c.get ("tree-hint")->scope () : c.scope (); } semantics::path context:: class_file (semantics::class_& c) { // If we have an explicit definition location, use that. // if (c.count ("definition")) return semantics::path (LOCATION_FILE (c.get ("definition"))); // // Otherwise, if it is a template instantiation, use the location // of the qualifier. // else if (c.is_a ()) return semantics::path (LOCATION_FILE (c.get ("location"))); else return c.file (); } location_t context:: class_location (semantics::class_& c) { return c.count ("definition") ? c.get ("definition") : class_real_location (c); } location_t context:: class_real_location (semantics::class_& c) { return c.is_a () ? c.get ("location") : real_source_location (TYPE_NAME (c.tree_node ())); } string context:: upcase (string const& s) { string r; string::size_type n (s.size ()); r.reserve (n); for (string::size_type i (0); i < n; ++i) r.push_back (toupper (s[i])); return r; } void context:: diverge (streambuf* sb) { data_->os_stack_.push (data_->os_.rdbuf ()); data_->os_.rdbuf (sb); } void context:: restore () { data_->os_.rdbuf (data_->os_stack_.top ()); data_->os_stack_.pop (); } semantics::type& context:: utype (semantics::type& t) { if (semantics::qualifier* q = dynamic_cast (&t)) return q->base_type (); else return t; } semantics::type& context:: utype (semantics::type& t, semantics::names*& hint) { if (semantics::qualifier* q = dynamic_cast (&t)) { hint = q->qualifies ().hint (); return q->base_type (); } else return t; } semantics::type& context:: utype (semantics::data_member& m, semantics::names*& hint, string const& kp, const custom_cxx_type** translation) { semantics::type* t (0); if (kp.empty ()) { t = &m.type (); if (semantics::qualifier* q = dynamic_cast (t)) { hint = q->qualifies ().hint (); t = &q->base_type (); } else hint = m.belongs ().hint (); } else { if (m.count (kp + "-tree-type")) t = indirect_type (m, kp, hint); else { t = &utype (m); // "See through" wrappers. // if (semantics::type* wt = wrapper (*t)) t = indirect_type (utype (*wt), kp, hint); else t = indirect_type (*t, kp, hint); } } // Do we need to map this type? // // @@ Need to cache the result on the member. // if (translation != 0) *translation = 0; for (semantics::scope* s (&m.scope ());; s = &s->scope_ ()) { using semantics::namespace_; if (namespace_* ns = dynamic_cast (s)) { if (ns->extension ()) s = &ns->original (); } if (s->count ("custom-cxx-type-map")) { typedef custom_cxx_type_map map; map& m (s->get ("custom-cxx-type-map")); map::const_iterator i (m.find (t)); if (i != m.end ()) { hint = i->second->as_hint; t = i->second->as; if (translation != 0) *translation = i->second; // Currently we only support one level of mapping, but I am // sure someone will want multiple levels. // break; } } if (s->global_scope ()) break; } return *t; } bool context:: const_type (semantics::type& t) { if (semantics::qualifier* q = dynamic_cast (&t)) return q->const_ (); return false; } string context:: type_ref_type (semantics::type& t, semantics::names* hint, bool mc, string const& var) { using semantics::array; string r; // Note that trailing const syntax is used for a reason (consider // t == const foo*). We also have to decay top-level arrays. // if (array* a = dynamic_cast (&utype (t))) { semantics::type& bt (a->base_type ()); hint = a->contains ().hint (); if (bt.is_a ()) { // If we need to add/strip const or no name was used in the // declaration, then create an array declaration (e.g., for // char x[2][3] we will have char const (*x)[3]). // if (mc != const_type (t) || hint == 0) return type_val_type (bt, 0, mc, "(*" + var + ")"); } // Array base type is always cvr-unqualified. // if (mc) r = bt.fq_name (hint) + " const"; else r = bt.fq_name (hint); r += '*'; if (!var.empty ()) r += ' ' + var; } else { if (mc == const_type (t)) r = t.fq_name (hint); else if (mc) r = t.fq_name (hint) + " const"; else { semantics::type& ut (utype (t, hint)); r = ut.fq_name (hint); } r += '&'; if (!var.empty ()) r += ' ' + var; } return r; } string context:: type_val_type (semantics::type& t, semantics::names* hint, bool mc, string const& var) { using semantics::array; string r; // Arrays are a complicated case. Firstly, we may need to add/strip const // to/from the base type. Secondly, the array dimensions are written after // the variable name. All this is further complicated by multiple dimensions. // Thanks, Dennis! // if (array* a = dynamic_cast (&utype (t))) { semantics::type& bt (a->base_type ()); // If we don't need to add/strip const and a name was used in the // declaration, then use that name. // if (mc == const_type (t) && hint != 0) { r = t.fq_name (hint); if (!var.empty ()) r += ' ' + var; } else { // Otherwise, construct the array declaration. // string v (var); v += '['; ostringstream ostr; ostr << a->size (); v += ostr.str (); if (a->size () > 0xFFFFFFFF) v += "ULL"; else if (a->size () > 2147483647) v += "U"; v += ']'; r = type_val_type (bt, a->contains ().hint (), mc, v); } } else { if (mc == const_type (t)) r = t.fq_name (hint); else if (mc) r = t.fq_name (hint) + " const"; else { semantics::type& ut (utype (t, hint)); r = ut.fq_name (hint); } if (!var.empty ()) r += ' ' + var; } return r; } void context:: set_member (semantics::data_member& m, const string& obj, const string& val, const string& db, const string& type) { member_access& ma (m.get ("set")); // If this is a custom expression, output the location of where // it came from. // if (!ma.synthesized) os << "// From " << location_string (ma.loc, true) << endl; if (ma.placeholder ()) { // Cast the database to the concrete type this code is generated // for. This way the user is free to use either the concrete or // the common. // string d; if (!db.empty ()) d = "static_cast<" + context::db.string () + "::database&> (" + db + ")"; os << ma.translate (obj, val, d) << ";"; } else { // If this member is const and we have a synthesized direct access, // then cast away constness. Otherwise, we assume that the user- // provided expression handles this. // bool cast (!type.empty () && ma.direct () && const_member (m)); if (cast) os << "const_cast< " << type << "& > (" << endl; os << ma.translate (obj); if (cast) os << ")"; os << " = " << val << ";"; } } void context:: inc_member (semantics::data_member& m, const string& obj, const string& gobj, const string& type) { member_access& ma (m.get ("set")); // If this is a custom expression, output the location of where // it came from. // if (!ma.synthesized) os << "// From " << location_string (ma.loc, true) << endl; if (ma.placeholder ()) { member_access& gma (m.get ("get")); if (!gma.synthesized) os << "// From " << location_string (gma.loc, true) << endl; os << ma.translate (obj, gma.translate (gobj) + " + 1") << ";"; } else { // If this member is const and we have a synthesized direct access, // then cast away constness. Otherwise, we assume that the user- // provided expression handles this. // os << "++"; bool cast (ma.direct () && const_member (m)); if (cast) os << "const_cast< " << type << "& > (" << endl; os << ma.translate (obj); if (cast) os << ")"; os << ";"; } } void context:: resolve_data_members (data_member_path& r, semantics::class_& c, const string& name, const location& l, cxx_string_lexer& lex) { using semantics::class_; using semantics::data_member; // The name was already verified to be syntactically correct so // we don't need to do any extra error checking in this area. // lex.start (name); try { string tl; cpp_ttype tt (lex.next (tl)); data_member& m (c.lookup (tl, class_::include_hidden)); r.push_back (&m); if (container (m)) return; // Resolve nested members if any. // for (tt = lex.next (tl); tt == CPP_DOT; tt = lex.next (tl)) { lex.next (tl); // Get CPP_NAME. data_member& om (*r.back ()); // Check that the outer member is composite and also unwrap it while // at it. // class_* comp (composite_wrapper (utype (om))); if (comp == 0) { error (l) << "data member '" << om.name () << "' is not composite" << endl; throw operation_failed (); } data_member& nm ( comp->lookup (tl, class_::include_hidden)); r.push_back (&nm); if (container (nm)) return; } } catch (semantics::unresolved const& e) { if (e.type_mismatch) error (l) << "name '" << e.name << "' does not refer to a data member" << endl; else error (l) << "unable to resolve data member '" << e.name << endl; throw operation_failed (); } catch (semantics::ambiguous const& e) { error (l) << "data member name '" << e.first.name () << "' is ambiguous" << endl; info (e.first.named ().location ()) << "could resolve to " << "this data member" << endl; info (e.second.named ().location ()) << "or could resolve " << "to this data member" << endl; throw operation_failed (); } } bool context:: composite_ (semantics::class_& c) { bool r (c.count ("value") && !c.count ("simple") && !c.count ("container")); c.set ("composite-value", r); return r; } // context::table_prefix // context::table_prefix:: table_prefix (semantics::class_& c) : level (1) { context& ctx (context::current ()); ns_schema = ctx.schema (class_scope (c)); ns_prefix = ctx.table_name_prefix (class_scope (c)); prefix = ctx.table_name (c, &derived); prefix += "_"; } void context::table_prefix:: append (semantics::data_member& m) { assert (level > 0); context& ctx (context::current ()); // If a custom table prefix was specified, then ignore the top-level // table prefix (this corresponds to a container directly inside an // object) but keep the schema unless the alternative schema is fully // qualified. // if (m.count ("table")) { qname p, n (m.get ("table")); if (n.fully_qualified ()) p = n.qualifier (); else { if (n.qualified ()) { p = ns_schema; p.append (n.qualifier ()); } else p = prefix.qualifier (); } if (level == 1) { p.append (ns_prefix); derived = false; } else p.append (prefix.uname ()); p += n.uname (); prefix.swap (p); } // Otherwise use the member name and add an underscore unless it is // already there. // else { string name (ctx.public_name_db (m)); size_t n (name.size ()); prefix += name; if (n != 0 && name[n - 1] != '_') prefix += "_"; derived = true; } level++; } qname context:: schema (semantics::scope& s) const { if (s.count ("qualified-schema")) return s.get ("qualified-schema"); qname r; for (semantics::scope* ps (&s);; ps = &ps->scope_ ()) { using semantics::namespace_; namespace_* ns (dynamic_cast (ps)); if (ns == 0) continue; // Some other scope. if (ns->extension ()) ns = &ns->original (); bool sf (ns->count ("schema")); bool tf (ns->count ("table")); if (tf) { qname n (ns->get ("table")); tf = n.qualified (); // If we have both schema and qualified table prefix, see which // takes precedence based on order. // if (tf && sf) { if (ns->get ("table-location") > ns->get ("schema-location")) sf = false; else tf = false; } } if (sf || tf) { qname n ( sf ? ns->get ("schema") : ns->get ("table").qualifier ()); n.append (r); n.swap (r); } if (r.fully_qualified () || ns->global_scope ()) break; } // If we are still not fully qualified, add the schema that was // specified on the command line. // if (!r.fully_qualified () && options.schema ().count (db) != 0) { qname n (options.schema ()[db]); n.append (r); n.swap (r); } s.set ("qualified-schema", r); return r; } string context:: table_name_prefix (semantics::scope& s) const { if (s.count ("table-prefix")) return s.get ("table-prefix"); string r; for (semantics::scope* ps (&s);; ps = &ps->scope_ ()) { using semantics::namespace_; namespace_* ns (dynamic_cast (ps)); if (ns == 0) continue; // Some other scope. if (ns->extension ()) ns = &ns->original (); if (ns->count ("table")) { qname n (ns->get ("table")); r = n.uname () + r; } if (ns->global_scope ()) break; } // Add the prefix that was specified on the command line. // if (options.table_prefix ().count (db) != 0) r = options.table_prefix ()[db] + r; s.set ("table-prefix", r); return r; } qname context:: table_name (semantics::class_& c, bool* pd) const { if (c.count ("qualified-table")) return c.get ("qualified-table"); qname r; bool sf (c.count ("schema")); bool derived; if (c.count ("table")) { r = c.get ("table"); if (sf) { // If we have both schema and qualified table, see which takes // precedence based on order. If the table is unqualifed, then // add the schema. // sf = !r.qualified () || c.get ("table-location") < c.get ("schema-location"); } derived = false; } else { r = class_name (c); derived = true; } if (sf) { qname n (c.get ("schema")); n.append (r.uname ()); n.swap (r); } // Unless we are fully qualified, add any schemas that were // specified on the namespaces and/or with the command line // option. // if (!r.fully_qualified ()) { qname n (schema (class_scope (c))); n.append (r); n.swap (r); } // Add the table prefix if any. // r.uname () = table_name_prefix (class_scope (c)) + r.uname (); if (derived) r.uname () = transform_name (r.uname (), sql_name_table); c.set ("qualified-table", r); if (pd != 0) *pd = derived; return r; } qname context:: table_name (semantics::class_& obj, data_member_path const& mp) const { table_prefix tp (obj); if (mp.size () == 1) { // Container directly in the object. // return table_name (*mp.back (), tp); } else { data_member_path::const_iterator i (mp.begin ()); // The last member is the container. // for (data_member_path::const_iterator e (mp.end () - 1); i != e; ++i) tp.append (**i); return table_name (**i, tp); } } // The table prefix passed as the second argument must include the table // prefix specified on namespaces and with the --table-prefix option. // qname context:: table_name (semantics::data_member& m, table_prefix const& p) const { assert (p.level > 0); qname r; string rn; bool derived; // Any of the components in the table name is derived. // If a custom table name was specified, then ignore the top-level // table prefix (this corresponds to a container directly inside an // object). If the container table is unqualifed, then we use the // object schema. If it is fully qualified, then we use that name. // Finally, if it is qualified but not fully qualifed, then we // append the object's namespace schema. // if (m.count ("table")) { qname n (m.get ("table")); if (n.fully_qualified ()) r = n.qualifier (); else { if (n.qualified ()) { r = p.ns_schema; r.append (n.qualifier ()); } else r = p.prefix.qualifier (); } if (p.level == 1) { rn = p.ns_prefix; derived = false; } else { rn = p.prefix.uname (); derived = p.derived; } rn += n.uname (); } else { r = p.prefix.qualifier (); rn = p.prefix.uname () + public_name_db (m); derived = true; } if (derived) r.append (transform_name (rn, sql_name_table)); else r.append (rn); return r; } // context::column_prefix // context::column_prefix:: column_prefix (data_member_path const& mp, bool l) : derived (false), underscore (false) { if (mp.size () < (l ? 1 : 2)) return; for (data_member_path::const_iterator i (mp.begin ()), e (mp.end () - (l ? 0 : 1)); i != e; ++i) append (**i); } void context::column_prefix:: append (semantics::data_member& m, string const& kp, string const& dn) { bool d; context& ctx (context::current ()); if (kp.empty ()) prefix += ctx.column_name (m, d); else prefix += ctx.column_name (m, kp, dn, d); // If the user provided the column prefix, then use it verbatime. // Otherwise, append the underscore, unless it is already there. // underscore = false; if (d) { size_t n (prefix.size ()); if (n != 0 && prefix[n - 1] != '_') { prefix += '_'; underscore = true; } } derived = derived || d; } string context:: column_name (semantics::data_member& m, bool& derived) const { derived = !m.count ("column"); return derived ? public_name_db (m) : m.get ("column").column; } string context:: column_name (semantics::data_member& m, column_prefix const& cp) const { bool d; const string& cn (column_name (m, d)); string n (cp.prefix); if (cn.empty () && cp.underscore) n.resize (n.size () - 1); // Strip underscore that was auto added. n += cn; // If any component is derived, then run it through the SQL name regex. // if (d || cp.derived) n = transform_name (n, sql_name_column); return n; } string context:: column_name (semantics::data_member& m, string const& p, string const& d, bool& derived) const { if (p.empty () && d.empty ()) return column_name (m, derived); // A container column name can be specified for the member or for the // container type. // string key (p + "-column"); derived = false; if (m.count (key)) return m.get (key); else { semantics::type& t (utype (m)); if (t.count (key)) return t.get (key); } derived = true; return d; } string context:: column_name (semantics::data_member& m, string const& kp, string const& dn, column_prefix const& cp) const { bool d; const string& cn (column_name (m, kp, dn, d)); string n (cp.prefix); if (cn.empty () && cp.underscore) n.resize (n.size () - 1); // Strip underscore that was auto-added. n += cn; // If any component is derived, the run it through the SQL name regex. // if (d || cp.derived) n = transform_name (n, sql_name_column); return n; } string context:: column_name (data_member_path const& mp) const { return column_name (*mp.back (), column_prefix (mp)); } string context:: column_type (const data_member_path& mp, string const& kp, bool id) { if (kp.empty ()) { // Return the id type if this member is or is a part of an object id // or pointer to object. // return mp.back ()->get ( id || context::id (mp) || object_pointer (mp) ? "column-id-type" : "column-type"); } else return indirect_value (*mp.back (), kp + "-column-type"); } string context:: column_type (semantics::data_member& m, string const& kp) { return kp.empty () ? m.get ("column-type") : indirect_value (m, kp + "-column-type"); } string context:: column_options (semantics::data_member& m) { // Accumulate options from both type and member. // semantics::type& t (utype (m)); string r; if (t.count ("options")) { strings const& o (t.get ("options")); for (strings::const_iterator i (o.begin ()); i != o.end (); ++i) { if (i->empty ()) r.clear (); else { if (!r.empty ()) r += ' '; r += *i; } } } if (m.count ("options")) { strings const& o (m.get ("options")); for (strings::const_iterator i (o.begin ()); i != o.end (); ++i) { if (i->empty ()) r.clear (); else { if (!r.empty ()) r += ' '; r += *i; } } } return r; } string context:: column_options (semantics::data_member& m, string const& kp) { if (kp.empty ()) return column_options (m); string k (kp + "-options"); // Accumulate options from type, container, and member. // semantics::type& c (utype (m)); semantics::type& t (utype (m, kp)); string r; if (t.count ("options")) { strings const& o (t.get ("options")); for (strings::const_iterator i (o.begin ()); i != o.end (); ++i) { if (i->empty ()) r.clear (); else { if (!r.empty ()) r += ' '; r += *i; } } } if (c.count (k)) { strings const& o (c.get (k)); for (strings::const_iterator i (o.begin ()); i != o.end (); ++i) { if (i->empty ()) r.clear (); else { if (!r.empty ()) r += ' '; r += *i; } } } if (m.count (k)) { strings const& o (m.get (k)); for (strings::const_iterator i (o.begin ()); i != o.end (); ++i) { if (i->empty ()) r.clear (); else { if (!r.empty ()) r += ' '; r += *i; } } } return r; } context::type_map_type::const_iterator context::type_map_type:: find (semantics::type& t, semantics::names* hint) { const_iterator e (end ()), i (e); // First check the hinted name. This allows us to handle things like // size_t which is nice to map to the same type irrespective of the // actual type. Since this type can be an alias for the one we are // interested in, go into nested hints. // for (; hint != 0 && i == e; hint = hint->hint ()) i = base::find (t.fq_name (hint)); // If the hinted name didn't work, try the primary name (e.g., // ::std::string) instead of a user typedef (e.g., my_string). // if (i == e) i = base::find (t.fq_name ()); return i; } string context:: database_type_impl (semantics::type& t, semantics::names* hint, bool id, bool* null) { using semantics::enum_; // By default map an enum as its underlying type. // if (enum_* e = dynamic_cast (&t)) return database_type_impl ( e->underlying_type (), e->underlying_type_hint (), id, null); // Built-in type mapping. // type_map_type::const_iterator i (data_->type_map_.find (t, hint)); if (i != data_->type_map_.end ()) { if (null != 0) *null = i->second.null; return id ? i->second.id_type : i->second.type; } return string (); } static string public_name_impl (semantics::data_member& m) { string s (m.name ()); size_t n (s.size ()); // Do basic processing: remove trailing and leading underscores // as well as the 'm_' prefix. // // @@ What if the resulting names conflict? // size_t b (0), e (n - 1); if (n > 2 && s[0] == 'm' && s[1] == '_') b += 2; for (; b <= e && s[b] == '_'; b++) ; for (; e >= b && s[e] == '_'; e--) ; return b > e ? s : string (s, b, e - b + 1); } string context:: public_name_db (semantics::data_member& m) const { return public_name_impl (m); } string context:: compose_name (string const& prefix, string const& name) { string r (prefix); size_t n (r.size ()); // Add an underscore unless one is already in the prefix or // the name is empty. Similarly, remove it if it is there but // the name is empty. // if (n != 0) { if (r[n - 1] != '_') { if (!name.empty ()) r += '_'; } else { if (name.empty ()) r.resize (n - 1); } } r += name; return r; } string context:: transform_name (string const& name, sql_name_type type) const { string r; if (!data_->sql_name_regex_[type].empty () || !data_->sql_name_regex_[sql_name_all].empty ()) { bool t (options.sql_name_regex_trace ()); if (t) cerr << "name: '" << name << "'" << endl; bool found (false); // First try the type-specific transformations, if that didn't work, // try common transformations. // for (unsigned short j (0); !found && j < 2; ++j) { regex_mapping const& rm = data_->sql_name_regex_[ j == 0 ? type : sql_name_all]; for (regex_mapping::const_iterator i (rm.begin ()); i != rm.end (); ++i) { if (t) cerr << "try: '" << i->regex () << "' : "; if (i->match (name)) { r = i->replace (name); found = true; if (t) cerr << "'" << r << "' : "; } if (t) cerr << (found ? '+' : '-') << endl; if (found) break; } } if (!found) r = name; } else r = name; if (options.sql_name_case ().count (db) != 0) { switch (options.sql_name_case ()[db]) { case name_case::upper: { r = data_->sql_name_upper_.replace (r); break; } case name_case::lower: { r = data_->sql_name_lower_.replace (r); break; } } } return r; } string context:: public_name (semantics::data_member& m, bool e) const { return e ? escape (public_name_impl (m)) : public_name_impl (m); } string context:: flat_name (string const& fq) { string r; r.reserve (fq.size ()); for (string::size_type i (0), n (fq.size ()); i < n; ++i) { char c (fq[i]); if (c == ':') { if (!r.empty ()) r += '_'; ++i; // Skip the second ':'. } else r += c; } return r; } string context:: escape (string const& name) const { typedef string::size_type size; string r; size n (name.size ()); // In most common cases we will have that many characters. // r.reserve (n); for (size i (0); i < n; ++i) { char c (name[i]); if (i == 0) { if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) r = (c >= '0' && c <= '9') ? "cxx_" : "cxx"; } if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) r += '_'; else r += c; } if (r.empty ()) r = "cxx"; // Custom reserved words. // /* reserved_name_map_type::const_iterator i (reserved_name_map.find (r)); if (i != reserved_name_map.end ()) { if (!i->second.empty ()) return i->second; else r += L'_'; } */ // Keywords // if (keyword_set.find (r) != keyword_set.end ()) { r += '_'; // Re-run custom words. // /* i = reserved_name_map.find (r); if (i != reserved_name_map.end ()) { if (!i->second.empty ()) return i->second; else r += L'_'; } */ } return r; } string context:: make_guard (string const& s) const { // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything // to upper case. // string r; for (string::size_type i (0), n (s.size ()); i < n - 1; ++i) { char c1 (s[i]); char c2 (s[i + 1]); r += toupper (c1); if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2)) r += "_"; } r += toupper (s[s.size () - 1]); return escape (r); } static string charlit (unsigned int u) { string r ("\\x"); bool lead (true); for (short i (7); i >= 0; --i) { unsigned int x ((u >> (i * 4)) & 0x0F); if (lead) { if (x == 0) continue; lead = false; } r += static_cast (x < 10 ? ('0' + x) : ('A' + x - 10)); } return r; } static string strlit_ascii (string const& str) { string r; string::size_type n (str.size ()); // In most common cases we will have that many chars. // r.reserve (n + 2); r += '"'; bool escape (false); for (string::size_type i (0); i < n; ++i) { unsigned int u (static_cast (str[i])); // [128 - ] - unrepresentable // 127 - \x7F // [32 - 126] - as is // [0 - 31] - \X or \xXX // if (u < 32 || u == 127) { switch (u) { case '\n': { r += "\\n"; break; } case '\t': { r += "\\t"; break; } case '\v': { r += "\\v"; break; } case '\b': { r += "\\b"; break; } case '\r': { r += "\\r"; break; } case '\f': { r += "\\f"; break; } case '\a': { r += "\\a"; break; } default: { r += charlit (u); escape = true; break; } } } else if (u < 127) { if (escape) { // Close and open the string so there are no clashes. // r += '"'; r += '"'; escape = false; } switch (u) { case '"': { r += "\\\""; break; } case '\\': { r += "\\\\"; break; } default: { r += static_cast (u); break; } } } else { // @@ Unrepresentable character. // r += '?'; } } r += '"'; return r; } string context:: strlit (string const& str) { return strlit_ascii (str); } void context:: inst_header (bool decl, bool omit_exp) { if (decl && !ext.empty ()) os << ext << " "; os << "template struct"; if (!omit_exp && !exp.empty ()) { // If we are generating an explicit instantiation directive rather // than the extern template declaration, then omit the export symbol // if we already have it in the header (i.e., extern symbol specified // and defined). If we don't do that, then we get GCC warnings saying // that the second set of visibility attributes is ignored. // if (!decl && !ext.empty ()) os << endl << "#ifndef " << ext << endl << options.export_symbol ()[db] << endl << "#endif" << endl; else os << " " << exp; } else os << " "; } namespace { struct column_count_impl: object_members_base { column_count_impl (object_section* section = 0) : object_members_base (false, section) { } virtual void traverse_pointer (semantics::data_member& m, semantics::class_& c) { // Object pointers in views require special treatment. // if (view_member (m)) { using semantics::class_; column_count_type cc; if (class_* root = polymorphic (c)) { // For a polymorphic class we are going to load all the members // from all the bases (i.e., equivalent to the first statement // in the list of SELECT statements generated for the object). // So our count should be the same as the first value in the // generated column_counts array. // for (class_* b (&c);; b = &polymorphic_base (*b)) { column_count_type const& ccb (column_count (*b, section_)); cc.total += ccb.total - (b != root ? ccb.id : 0); cc.separate_load += ccb.separate_load; cc.soft += ccb.soft; if (b == root) break; } } else cc = column_count (c, section_); c_.total += cc.total - cc.separate_load; if (added (member_path_) != 0 || deleted (member_path_) != 0) c_.soft += cc.total; else c_.soft += cc.soft; } else { size_t t (c_.total); object_members_base::traverse_pointer (m, c); if (context::inverse (m)) { size_t n (c_.total - t); c_.inverse += n; if (separate_update (member_path_)) c_.separate_update -= n; } } } virtual void traverse_simple (semantics::data_member& m) { c_.total++; bool ro (context::readonly (member_path_, member_scope_)); if (id ()) c_.id++; else if (ro) c_.readonly++; else if (context::version (m)) c_.optimistic_managed++; // For now discriminator can only be a simple value. // if (discriminator (m)) c_.discriminator++; { unsigned long long av (added (member_path_)); unsigned long long dv (deleted (member_path_)); // If the addition/deletion version is the same as the section's, // then don't count. // if (user_section* s = dynamic_cast (section_)) { if (av == added (*s->member)) av = 0; if (dv == deleted (*s->member)) dv = 0; } if (av != 0) c_.added++; if (dv != 0) c_.deleted++; if (av != 0 || dv != 0) c_.soft++; } if (separate_load (member_path_)) c_.separate_load++; if (separate_update (member_path_) && !ro) c_.separate_update++; } context::column_count_type c_; }; } context::column_count_type context:: column_count (semantics::class_& c, object_section* s) { if (s == 0) { // Whole class. // if (!c.count ("column-count")) { column_count_impl t; t.traverse (c); c.set ("column-count", t.c_); } return c.get ("column-count"); } else { column_count_impl t (s); t.traverse (c); return t.c_; } } namespace { struct has_a_impl: object_members_base { has_a_impl (unsigned short flags, object_section* s) : object_members_base ((flags & context::include_base) != 0, s), r_ (0), flags_ (flags) { } size_t result () const { return r_; } virtual bool section_test (data_member_path const& mp) { object_section& s (section (mp)); // Include eager loaded members into the main section if requested. // return section_ == 0 || *section_ == s || ((flags_ & include_eager_load) != 0 && *section_ == main_section && !s.separate_load ()); } virtual void traverse_pointer (semantics::data_member& m, semantics::class_&) { // Ignore polymorphic id references; they are represented as // pointers but are normally handled in a special way. // if (m.count ("polymorphic-ref")) return; // Ignore added/deleted members if so requested. // if (check_soft ()) return; if (context::is_a (member_path_, member_scope_, flags_)) r_++; // No need to go inside. } virtual void traverse_simple (semantics::data_member&) { // Ignore added/deleted members if so requested. // if (check_soft ()) return; if (context::is_a (member_path_, member_scope_, flags_)) r_++; } virtual void traverse_container (semantics::data_member& m, semantics::type&) { // Ignore added/deleted members if so requested. // if (check_soft ()) return; // Ignore versioned containers if so requested. // if ((flags_ & exclude_versioned) != 0 && versioned (m)) return; // We don't cross the container boundaries (separate table). // unsigned short f (flags_ & (context::test_container | context::test_straight_container | context::test_inverse_container | context::test_readonly_container | context::test_readwrite_container | context::test_smart_container)); if (context::is_a (member_path_, member_scope_, f, context::container_vt (m), "value")) r_++; } virtual void traverse_object (semantics::class_& c) { if ((flags_ & context::exclude_base) == 0) inherits (c); names (c); } private: bool check_soft () { if ((flags_ & exclude_added) != 0 || (flags_ & exclude_deleted) != 0) { unsigned long long av (added (member_path_)); unsigned long long dv (deleted (member_path_)); // If the addition/deletion version is the same as the section's, // then don't exclude. // if (user_section* s = dynamic_cast (section_)) { if (av == added (*s->member)) av = 0; if (dv == deleted (*s->member)) dv = 0; } if ((av != 0 && (flags_ & exclude_added) != 0) || (dv != 0 && (flags_ & exclude_deleted) != 0)) return true; } return false; } private: size_t r_; unsigned short flags_; }; } bool context:: is_a (data_member_path const& mp, data_member_scope const& ms, unsigned short f, semantics::type& t, string const& kp) { bool r (false); semantics::data_member& m (*mp.back ()); if (f & test_pointer) r = r || object_pointer (t); if (f & test_eager_pointer) r = r || (object_pointer (t) && !lazy_pointer (t)); if (f & test_lazy_pointer) r = r || (object_pointer (t) && lazy_pointer (t)); semantics::type* c; if ((f & (test_container | test_straight_container | test_inverse_container | test_readonly_container | test_readwrite_container | test_smart_container)) != 0 && (c = container (m)) != 0) { if (f & test_container) r = r || true; if (f & test_straight_container) r = r || !inverse (m, kp); if (f & test_inverse_container) r = r || inverse (m, kp); if (f & test_readonly_container) r = r || readonly (mp, ms); if (f & test_readwrite_container) r = r || (!inverse (m, kp) && !readonly (mp, ms)); if (f & test_smart_container) r = r || (!inverse (m, kp) && !unordered (m) && container_smart (*c)); } return r; } size_t context:: has_a (semantics::class_& c, unsigned short flags, object_section* s) { has_a_impl impl (flags, s); impl.dispatch (c); return impl.result (); } string context:: process_include_path (string const& ip, bool prefix, char open) { bool t (options.include_regex_trace ()); string p (prefix ? options.include_prefix () : string ()); if (!p.empty () && p[p.size () - 1] != '/') p.append ("/"); string path (p + ip), r; if (t) cerr << "include: '" << path << "'" << endl; bool found (false); for (regex_mapping::const_iterator i (include_regex.begin ()); i != include_regex.end (); ++i) { if (t) cerr << "try: '" << i->regex () << "' : "; if (i->match (path)) { r = i->replace (path); found = true; if (t) cerr << "'" << r << "' : "; } if (t) cerr << (found ? '+' : '-') << endl; if (found) break; } if (!found) r = path; // Add brackets or quotes unless the path already has them. // if (!r.empty () && r[0] != '"' && r[0] != '<') { bool b (open == '<' || (open == '\0' && options.include_with_brackets ())); char op (b ? '<' : '"'), cl (b ? '>' : '"'); r = op + r + cl; } return r; }