NCBI C++ ToolKit
obj_convert.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

00001 /*  $Id: obj_convert.cpp 27799 2013-04-03 20:57:06Z wuliangs $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Authors:  Mike DiCuccio
00027  *
00028  * File Description:
00029  *
00030  */
00031 
00032 #include <ncbi_pch.hpp>
00033 #include <corelib/ncbimtx.hpp>
00034 #include <gui/objutils/obj_convert.hpp>
00035 #include "convert_graph.hpp"
00036 #include <serial/iterator.hpp>
00037 
00038 #include <algorithm>
00039 
00040 
00041 
00042 BEGIN_NCBI_SCOPE
00043 USING_SCOPE(objects);
00044 
00045 
00046 DEFINE_STATIC_MUTEX(s_ObjCvtMutex);
00047 CObjectConverter::TTypeAliases CObjectConverter::sm_TypeAliases;
00048 CObjectConverter::TFlags       CObjectConverter::sm_DefaultFlags = ITypeConverter::eDefault;
00049 
00050 CObjectConverter::TRelationVector CObjectConverter::sm_Relations;
00051 
00052 // New Interface
00053 //////////////////////////////////////////////////////////////////////////
00054 //
00055 // converter registration
00056 //
00057 
00058 void CObjectConverter::Register(CRelation* rel)
00059 {
00060     CMutexGuard LOCK(s_ObjCvtMutex);
00061     _ASSERT(numeric_limits<size_t>::max() == x_FindRelationByName(rel->GetName()));
00062     sm_Relations.push_back(CRef<CRelation>(rel));
00063 }
00064 
00065 const CRelation* CObjectConverter::FindRelationByName(const string& name)
00066 {
00067     CMutexGuard LOCK(s_ObjCvtMutex);
00068 
00069     size_t idx = x_FindRelationByName(name);
00070     if (idx == numeric_limits<size_t>::max())
00071         return 0;
00072 
00073     return sm_Relations[idx];
00074 }
00075 
00076 
00077 static bool PCompare(const CConvGraph::TPath& p1, const CConvGraph::TPath& p2)
00078 {
00079     return p1.size() < p2.size();
00080 }
00081 
00082 static void x_BuildRelations
00083     (const CObjectConverter::TRelationVector& registered,
00084      const vector<CConvGraph::TPath>& paths,
00085      CObjectConverter::TRelationVector& relations)
00086 {
00087     if (paths.size() == 0)
00088         return;
00089 
00090     // Find out if there are several paths with smallest length
00091     size_t count = 0, length;
00092     length = paths[0].size();
00093     ITERATE(vector<CConvGraph::TPath>, iter, paths) {
00094         if ((*iter).size() > length)
00095             break;
00096         ++count;
00097     }
00098 
00099     // Add composite relation with paths of smallest length
00100     CRef<CComplexRelation> composite(new CComplexRelation(true));
00101     if (count > 1)
00102         relations.push_back(CRef<CRelation>(composite));
00103 
00104     ITERATE(vector<CConvGraph::TPath>, iter, paths) {
00105         if ((*iter).size() > length)
00106             break;
00107 
00108         CRef<CRelation> rel;
00109 
00110         if ((*iter).size() == 1) {
00111             rel = registered[(*iter).front()];
00112         }
00113         else {
00114             CRef<CComplexRelation> complex(new CComplexRelation());
00115             ITERATE(CConvGraph::TPath, iter2, *iter)
00116                 complex->AddRelation(registered[*iter2]);
00117             rel = complex;
00118         }
00119 
00120         relations.push_back(rel);
00121         if (count > 1)
00122             composite->AddRelation(rel);
00123     }
00124 }
00125 
00126 void CObjectConverter::FindRelations(objects::CScope& scope,
00127                                      const CObject& obj,
00128                                      const string& to_type_in,
00129                                      TRelationVector& relations)
00130 {
00131     CMutexGuard LOCK(s_ObjCvtMutex);
00132 
00133     size_t idxIdentity = x_FindRelationByName(CIdentityRelation::m_Name);
00134     if (idxIdentity == numeric_limits<size_t>::max())
00135         return;
00136 
00137     const string& to_type = x_NormalizeTypeName(to_type_in);
00138 
00139     const CSerialObject* so = dynamic_cast<const CSerialObject*>(&obj);
00140     string from_type = so ? so->GetThisTypeInfo()->GetName() : typeid(obj).name();
00141 
00142     CConvGraph graph;
00143     map<string, size_t> vertices;
00144     x_BuildGraph(graph, vertices);
00145 
00146     map<string, size_t>::const_iterator it;
00147     it = vertices.find(from_type);
00148     size_t from_vertex = (it != vertices.end()) ?
00149         it->second : numeric_limits<size_t>::max();
00150 
00151     it = vertices.find(to_type);
00152     size_t to_vertex = (it != vertices.end()) ?
00153         it->second : numeric_limits<size_t>::max();
00154 
00155     //
00156     // check to see if our object is a serial object first
00157     //
00158 
00159     vector<CConvGraph::TPath> paths;
00160 
00161     if (so && (to_type == "Object" || to_type == "SerialObject")) {
00162         graph.FindPaths(from_vertex, to_vertex, paths);
00163         if (paths.size() > 0 && paths[0].size() <= 2)
00164             x_BuildRelations (sm_Relations, paths, relations);
00165         else
00166             relations.push_back(sm_Relations[idxIdentity]);
00167         return;
00168     }
00169 
00170     // If types not present among converters we still can handle identity case
00171     if (from_type == to_type) {
00172         relations.push_back(sm_Relations[idxIdentity]);
00173         return;
00174     }
00175 
00176     graph.FindPaths(from_vertex, to_vertex, paths);
00177 
00178     if (paths.size() > 0) {
00179         x_BuildRelations (sm_Relations, paths, relations);
00180         return;
00181     }
00182 
00183     //
00184     // Handle Container Object (CDocument, CGBProjectHandle, CProjectItem)
00185     //
00186 
00187     size_t idxContainer2Object = x_FindRelationByName("Any Container --> Object");
00188     if (idxContainer2Object == numeric_limits<size_t>::max())
00189         return;
00190 
00191     CRelation::TObjects related;
00192     sm_Relations[idxContainer2Object]->GetRelated(scope, obj, related);
00193     if (related.size() == 0)
00194         return;
00195 
00196     set<CConvGraph::TPath> pathSet;
00197 
00198     ITERATE(CRelation::TObjects, iter, related) {
00199         const CSerialObject* so =
00200             dynamic_cast<const CSerialObject*>(iter->GetObjectPtr());
00201         from_type = so ? so->GetThisTypeInfo()->GetName() : typeid(**iter).name();
00202 
00203         if (from_type == to_type) {
00204             pathSet.insert(CConvGraph::TPath(1, idxIdentity));
00205             continue;
00206         }
00207 
00208         it = vertices.find(from_type);
00209         from_vertex = (it != vertices.end()) ?
00210             it->second : numeric_limits<size_t>::max();
00211 
00212         vector<CConvGraph::TPath> paths2;
00213         graph.FindPaths(from_vertex, to_vertex, paths2);
00214         ITERATE(vector<CConvGraph::TPath>, iter2, paths2)
00215             pathSet.insert(*iter2);
00216     }
00217 
00218     if (pathSet.size() == 0)
00219         return;
00220 
00221     // paths.insert(paths.begin(), pathSet.begin(), pathSet.end());
00222     ITERATE(set<CConvGraph::TPath>, it, pathSet)
00223         paths.push_back(*it);
00224 
00225     NON_CONST_ITERATE(vector<CConvGraph::TPath>, it, paths)
00226         (*it).insert((*it).begin(), idxContainer2Object);
00227 
00228     sort(paths.begin(), paths.end(), PCompare);
00229     x_BuildRelations (sm_Relations, paths, relations);
00230 }
00231 
00232 void CObjectConverter::DumpDotGraph(ostream& ostream, bool dumpIDs)
00233 {
00234     CMutexGuard LOCK(s_ObjCvtMutex);
00235 
00236     CConvGraph graph;
00237     map<string, size_t> vertices;
00238     x_BuildGraph(graph, vertices);
00239 
00240     ostream << "digraph {" << endl;
00241     if (dumpIDs) {
00242         ITERATE(TRelationVector, iter, sm_Relations) {
00243             ostream << "    \"" << vertices[(*iter)->GetTypeName()]
00244                  << "\" -> \"" << vertices[(*iter)->GetRelatedTypeName()] << "\";" << endl;
00245         }
00246     }
00247     else {
00248         ITERATE(TRelationVector, iter, sm_Relations) {
00249             ostream << "    \"" << (*iter)->GetTypeName()
00250                  << "\" -> \"" << (*iter)->GetRelatedTypeName() << "\";" << endl;
00251         }
00252     }
00253     ostream << "}" << endl;
00254 }
00255 
00256 // Old Interface
00257 //////////////////////////////////////////////////////////////////////////
00258 //
00259 // converter registration
00260 //
00261 
00262 void CObjectConverter::Register(const CTypeInfo* from_type,
00263                                 const CTypeInfo* to_type,
00264                                 ITypeConverter* cvt)
00265 {
00266     Register(from_type->GetName(), to_type->GetName(), cvt);
00267 }
00268 
00269 
00270 void CObjectConverter::Register(const string& from_type,
00271                                 const CTypeInfo* to_type,
00272                                 ITypeConverter* cvt)
00273 {
00274     Register(from_type, to_type->GetName(), cvt);
00275 }
00276 
00277 
00278 void CObjectConverter::Register(const CTypeInfo* from_type,
00279                                 const string& to_type,
00280                                 ITypeConverter* cvt)
00281 {
00282     Register(from_type->GetName(), to_type, cvt);
00283 }
00284 
00285 class NCBI_GUIOBJUTILS_EXPORT CRelationTypeConverterAdapter : public CRelation
00286 {
00287 public:
00288     CRelationTypeConverterAdapter() {}
00289     CRelationTypeConverterAdapter(const string& from_type,
00290                                   const string& to_type,
00291                                   ITypeConverter* typeConverter) :
00292         m_FromType(from_type),
00293         m_ToType(to_type),
00294         m_TypeConverter(typeConverter) {}
00295 
00296     virtual string    GetName() const { return typeid(m_TypeConverter.GetObject()).name(); }
00297     virtual string    GetDescription() const { return "ITypeConverterAdaptor"; }
00298 
00299     virtual string    GetTypeName() const { return m_FromType; }
00300     virtual string    GetRelatedTypeName() const { return m_ToType; }
00301 
00302     virtual void      GetRelated(objects::CScope& scope, const CObject& obj,
00303                                  TObjects& related,
00304                                  TFlags flags = eDefault,
00305                                  ICanceled* cancel = NULL) const;
00306 
00307     virtual void Dump(ostream& ostream) const
00308     {
00309         ostream << typeid(m_TypeConverter.GetObject()).name() << endl;
00310     }
00311 
00312 private:
00313     string m_FromType;
00314     string m_ToType;
00315     CConstRef<ITypeConverter> m_TypeConverter;
00316 };
00317 
00318 void CRelationTypeConverterAdapter::GetRelated(
00319         objects::CScope& scope,
00320         const CObject& obj,
00321         TObjects& related,
00322         TFlags flags,
00323         ICanceled*) const
00324 {
00325     ITypeConverter::TObjList obj_list;
00326     m_TypeConverter->Convert(scope, obj, obj_list, flags);
00327     ITERATE(ITypeConverter::TObjList, iter, obj_list) {
00328         related.push_back(SObject(iter->GetObject(), iter->GetComment()));
00329     }
00330 }
00331 
00332 void CObjectConverter::Register(const string& from_type,
00333                                 const string& to_type,
00334                                 ITypeConverter* cvt)
00335 {
00336     Register(new CRelationTypeConverterAdapter(from_type, to_type, cvt));
00337 }
00338 
00339 void CObjectConverter::RegisterTypeAlias(const string& from_type,
00340                                          const string& alias)
00341 {
00342     CMutexGuard LOCK(s_ObjCvtMutex);
00343     sm_TypeAliases[alias] = from_type;
00344 }
00345 
00346 
00347 void CObjectConverter::SetDefaultFlags(TFlags flags)
00348 {
00349     sm_DefaultFlags = flags;
00350 }
00351 
00352 
00353 CObjectConverter::TFlags CObjectConverter::GetDefaultFlags()
00354 {
00355     return sm_DefaultFlags;
00356 }
00357 
00358 
00359 //////////////////////////////////////////////////////////////////////////
00360 //
00361 // object conversion
00362 //
00363 
00364 bool CObjectConverter::CanConvert(CScope& scope, const CObject& obj,
00365                                   const CTypeInfo* info)
00366 {
00367     return CanConvert(scope, obj, info->GetName());
00368 }
00369 
00370 
00371 bool CObjectConverter::CanConvert(CScope& scope, const CObject& obj,
00372                                   const string& to_type_in)
00373 {
00374     TRelationVector relations;
00375     FindRelations(scope, obj, to_type_in, relations);
00376     return (relations.size() > 0);
00377 }
00378 
00379 
00380 void CObjectConverter::Convert(CScope& scope, const CObject& obj,
00381                                const CTypeInfo* info, TObjList& objs,
00382                                TFlags flags)
00383 {
00384     Convert(scope, obj, info->GetName(), objs, flags);
00385 }
00386 
00387 
00388 void CObjectConverter::Convert(CScope& scope, const CObject& obj,
00389                                const string& to_type_in, TObjList& objs,
00390                                TFlags flags)
00391 {
00392     TRelationVector relations;
00393     FindRelations(scope, obj, to_type_in, relations);
00394 
00395 #if 0
00396     CNcbiOfstream ostr("C:\\temp\\conversions_log.txt", IOS_BASE::out | IOS_BASE::app);
00397     ostr << endl << endl;
00398 
00399     const CSerialObject* so = dynamic_cast<const CSerialObject*>(&obj);
00400     string from_type = so ? so->GetThisTypeInfo()->GetName() : typeid(obj).name();
00401     ostr << "Conversion: " << from_type << " -- > " << to_type_in << endl << "{" << endl;
00402 
00403     //int count = min((size_t)3, relations.size());
00404     int count = relations.size();
00405     if (count == 0)
00406         ostr << "*** Conversion not found ***" << endl;
00407     else {
00408         for (int i = 0; i < count; ++i) {
00409             relations[i]->Dump(ostr);
00410             ostr << endl;
00411         }
00412     }
00413 
00414     ostr << "}" << endl;
00415 #endif
00416 
00417     if (relations.size() == 0)
00418         return;
00419 
00420     CRelation::TObjects related;
00421     relations[0]->GetRelated(scope, obj, related, flags);
00422 
00423     ITERATE(CRelation::TObjects, iter, related) {
00424         objs.push_back(ITypeConverter::SObject(iter->GetObject(),
00425                                                iter->GetComment()));
00426     }
00427 }
00428 
00429 void CObjectConverter::x_BuildGraph(CConvGraph& graph, map<string, size_t>& vertices)
00430 {
00431     ITERATE(TRelationVector, iter, sm_Relations) {
00432         size_t from_index, to_index;
00433         string from_type = (*iter)->GetTypeName();
00434         string to_type = (*iter)->GetRelatedTypeName();
00435 
00436         map<string, size_t>::const_iterator it = vertices.find(from_type);
00437         if (it == vertices.end()) {
00438             from_index = vertices.size();
00439             vertices[from_type] = from_index;
00440         }
00441         else
00442             from_index = it->second;
00443 
00444         it = vertices.find(to_type);
00445         if (it == vertices.end()) {
00446             to_index = vertices.size();
00447             vertices[to_type] = to_index;
00448         }
00449         else
00450             to_index = it->second;
00451 
00452         graph.add_edge(from_index, to_index);
00453     }
00454 }
00455 
00456 size_t CObjectConverter::x_FindRelationByName(const string& name)
00457 {
00458     ITERATE(TRelationVector, iter, sm_Relations) {
00459         if ((*iter)->GetName() == name) {
00460             return iter - sm_Relations.begin();
00461         }
00462     }
00463     return numeric_limits<size_t>::max();
00464 }
00465 
00466 const string& CObjectConverter::x_NormalizeTypeName(const string& str)
00467 {
00468     TTypeAliases::const_iterator iter = sm_TypeAliases.find(str);
00469     if (iter != sm_TypeAliases.end()) {
00470         return iter->second;
00471     }
00472 
00473     return str;
00474 }
00475 
00476 //////////////////////////////////////////////////////////////////////////
00477 //
00478 //  conversion cache
00479 //
00480 const CConvertCache::TObjList&
00481 CConvertCache::Convert(CScope& scope,
00482                        const CObject& obj,
00483                        const CTypeInfo* info,
00484                        CObjectConverter::TFlags flags)
00485 {
00486     if (info) {
00487         return Convert(scope, obj, info->GetName(), flags);
00488     }
00489     return m_EmptyObjList;
00490 }
00491 
00492 
00493 const CConvertCache::TObjList&
00494 CConvertCache::Convert(CScope& scope,
00495                        const CObject& obj,
00496                        const string& type_name,
00497                        CObjectConverter::TFlags flags)
00498 {
00499     SCacheKey key(scope, obj, type_name);
00500 
00501     CConvertCache::TCache::iterator pos;
00502     if ((pos = m_ObjCache.find(key)) == m_ObjCache.end()) {
00503         TCache::value_type val(key, m_EmptyObjList);
00504         CObjectConverter::Convert(scope, obj, type_name, val.second, flags);
00505         pair<CConvertCache::TCache::iterator, bool>
00506             r(m_ObjCache.insert(val));
00507 
00508         if (!r.second) {
00509             return m_EmptyObjList;
00510         }
00511         pos = r.first;
00512     }
00513 
00514     return pos->second;
00515 }
00516 
00517 
00518 bool CConvertCache::SCacheKeySort::operator() (const SCacheKey& key1,
00519                                                const SCacheKey& key_) const
00520 {
00521     if (key1.m_Scope.GetPointer() < key_.m_Scope.GetPointer()) {
00522         return true;
00523     }
00524 
00525     if (key1.m_Scope.GetPointer() >  key_.m_Scope.GetPointer()) {
00526         return false;
00527     }
00528 
00529     // key1.m_Scope == key_.m_Scope
00530     if (key1.m_Obj.GetPointer() < key_.m_Obj.GetPointer()) {
00531         return true;
00532     }
00533     if (key1.m_Obj.GetPointer() > key_.m_Obj.GetPointer()) {
00534         return false;
00535     }
00536 
00537     // (key1.m_Scope == key_.m_Scope) && (key1.m_Obj == key_.m_Obj)
00538     return (NStr::CompareCase(key1.m_Type, key_.m_Type) < 0);
00539 };
00540 
00541 END_NCBI_SCOPE
Modified on Tue Mar 31 13:42:13 2015 by modify_doxy.py rev. 426318